Converting Rows to Columns - sql-server

I have a table with columns UserID and CountryName
Now I want get record in this way
[UserId] [ContryName1] [ContryName2] [ContryName3].........

Fiddle here : http://sqlfiddle.com/#!6/cd6f1/1
DECLARE #SQL AS NVARCHAR(MAX)
WITH CTE AS
(
SELECT USERID,COUNTRYNAME,ROW_NUMBER() OVER(PARTITION BY USERID ORDER BY COUNTRYNAME) AS RN
FROM CNTRIES
)
SELECT #SQL = 'WITH CTE1 AS
(
SELECT USERID,COUNTRYNAME,ROW_NUMBER() OVER(PARTITION BY USERID ORDER BY COUNTRYNAME) AS RN
FROM CNTRIES
)
SELECT *
FROM
(SELECT USERID,COUNTRYNAME,RN FROM CTE1)C
PIVOT (MAX(COUNTRYNAME) FOR RN IN (['+STUFF((SELECT '],['+CAST(RN AS VARCHAR(100))
FROM CTE
GROUP BY RN
FOR XML PATH('')),1,3,'')+'])) AS PIVOTT'

PIVOT is your best option if your version is SQL Server 2005 or above, but you don't state the version and trying to use PIVOT without a natural aggregate can be difficult to grasp for some. If your version is below 2005, you have bigger problems. Otherwise, you'll need to left join the table on itself to give you the same result. You can use a ranking function to make it a little easier. Something like this, while inefficient, will produce similar results.
/*
IF OBJECT_ID('Countries','U') IS NOT NULL
DROP TABLE Countries
CREATE TABLE Countries
(
UserID INT
, CountryName VARCHAR(255)
)
INSERT Countries
VALUES (1, 'India')
, (1, 'UK')
, (2, 'USA')
, (2, 'India')
, (2, 'Canada')
*/
SELECT DISTINCT x.UserID, x.CountryName Country1, y.CountryName Country2, z.CountryName Country3
FROM Countries c
LEFT JOIN
(
SELECT *, RANK() OVER(PARTITION BY UserID ORDER BY UserID, CountryName) AS UserRank
FROM Countries
)x ON x.UserID = c.UserID AND x.UserRank=1
LEFT JOIN
(
SELECT *, RANK() OVER(PARTITION BY UserID ORDER BY UserID, CountryName) AS UserRank
FROM Countries
)y ON y.UserID = c.UserID AND y.UserRank=2
LEFT JOIN
(
SELECT *, RANK() OVER(PARTITION BY UserID ORDER BY UserID, CountryName) AS UserRank
FROM Countries
)z ON z.UserID = c.UserID AND z.UserRank=3

Related

sql server using recrusive cte to get the level in the same group

I have a sql server table showing the IDs and their previous IDs,
create table test2 ( ID varchar(10) ,
Pre_ID varchar(10)
)
insert into test2 values ('e','d')
, ('d','c')
, ('c','b')
, ('b','a')
, ('a',null)
, ('r','q')
, ('q','p')
, ('p',null)
the table is like this:
The result should be like this:
I have successfully got the levels using a recursive cte, but could not get the correct group for them. can anyone help? Thanks.
This is my code:
with cte as (
select id, Pre_ID, level
from #temp2
where Pre_ID is null
union all
select t2.id, t2.Pre_ID, t2.level
from cte
inner join #temp2 t2
on t2.Pre_ID=cte.id
)
select * from cte
order by id
What you need to do is start with the first level and add a ROW_NUMBER to that, then join all further levels recursively:
with cte as (
select id, Pre_ID, level, row_number() over (order by ID) as grp
from #temp2
where Pre_ID is null
union all
select t2.id, t2.Pre_ID, t2.level, cte.grp
from cte
inner join #temp2 t2
on t2.Pre_ID=cte.id
)
select * from cte
order by id;

Join Table with a CASE statement and based on the CASE, then SELECT a MAX value

I want to join a table on 4 conditions, of which 2 are very straightforward and 2 other conditions which are more difficult.
The first two:
PMCCONTRACTSTATUS.ContractID = CUSTAMOUNTREPORTTABLE.ContractID
PMCCONTRACTSTATUS.DATAAREAID = CUSTAMOUNTREPORTTABLE.DATAAREAID
The second two:
Only if the FROMDATE from Table PMCCONTRACTSTATUS is lower then the PERIODSTART in Table CUSTAMOUNTREPORTTABLE
AND only if that is the case THEN Take the highest RowNumber. EDIT: Because I want to JOIN the [STATUS] that is the CLOSEST to the PERIODSTART.
EDIT: I Made a sample dataset which only includes the necessary parts to solve the problem.
CREATE TABLE PMCCONTRACTSTATUSLOG2 (
ContractID nvarchar(20)
,[Status] int
,FromDate date
,RowNumber int
);
CREATE TABLE CUSTAMOUNTREPORTTABLE2 (
ContractID nvarchar(20)
,PERIODSTART date
);
INSERT INTO PMCCONTRACTSTATUSLOG2
(ContractID, [Status], FromDate, RowNumber)
VALUES
('HC1','1','01-01-2019','1'),
('HC1','2','01-02-2019','2'),
('HC1','1','01-04-2019','3'),
('HC2','1','01-04-2019','1'),
('HC2','2','01-05-2019','2'),
('HC3','4','01-01-2019','1'),
('HC3','2','01-02-2019','2'),
('HC3','1','01-07-2019','3'),
('HC3','2','01-09-2019','4'),
('HC4','2','01-08-2019','1'),
('HC4','3','01-07-2019','2'),
('HC5','1','01-02-2019','1');
INSERT INTO CUSTAMOUNTREPORTTABLE2
(ContractID, PERIODSTART)
VALUES
('HC1','01-01-2019'),
('HC2','01-01-2019'),
('HC3','01-01-2019'),
('HC5','01-01-2019'),
('HC1','01-02-2019'),
('HC3','01-02-2019'),
('HC5','01-02-2019'),
('HC1','01-03-2019'),
('HC3','01-03-2019'),
('HC5','01-03-2019'),
('HC1','01-04-2019'),
('HC2','01-04-2019'),
('HC3','01-04-2019'),
('HC5','01-04-2019'),
('HC1','01-05-2019'),
('HC2','01-05-2019'),
('HC3','01-05-2019'),
('HC5','01-05-2019');
SELECT * FROM PMCCONTRACTSTATUSLOG2
SELECT * FROM CUSTAMOUNTREPORTTABLE2
I try with the following query to join the PMCCONTRACTSTATUS Table, I know the logic behind the join, but I fail to write a JOIN that combines a CASE WHEN + SELECT statement.
;WITH PMCCONTRACTSTATUS AS
(
SELECT
CONTRACTID
,[STATUS]
,FROMDATE
,DATAAREAID
,ROW_NUMBER() OVER (PARTITION BY CONTRACTID ORDER BY FROMDATE ASC) RN
FROM PMCCONTRACTSTATUSLOG
WHERE [STATUS] <> 10 OR [STATUS] <> 5
)
SELECT
CART.CONTRACTID
,PMCCONTRACTSTATUS.CONTRACTID
,CART.ACCOUNTNUM
,CART.AMOUNTMSTTOTAL
,CART.PERIODSTART
,PMCCONTRACTSTATUS.FROMDATE
,CART.PERIODEND
,CART.NAME
,CART.PMCCONTRACTSTATUSWEIGHTED
,PMCCONTRACTSTATUS.[STATUS]
FROM CUSTAMOUNTREPORTTABLE CART
LEFT JOIN PMCCONTRACTSTATUS
ON PMCCONTRACTSTATUS.CONTRACTID = CART.CONTRACTID
AND PMCCONTRACTSTATUS.DATAAREAID = CART.DATAAREAID
/*AND CASE WHEN PMCCONTRACTSTATUS.FROMDATE <= CART.PERIODSTART
THEN (SELECT MAX(PMCCONTRACTSTATUSLOG.RN) FROM PMCCONTRACTSTATUSLOG)*/
Any suggestions?
This answer is based from my understanding, maybe some parts are missing :
First I try to join with your 3 first conditions in a CTE. And then I create a ROW_NUMBER() based on the DATEDIFF between your 2 dates.
Tell me if it helps :
;WITH
CTE AS (
SELECT
CART.CONTRACTID
,CART.ACCOUNTNUM
,CART.AMOUNTMSTTOTAL
,CART.PERIODSTART
,PMCCONTRACTSTATUSLOG.FROMDATE
,CART.PERIODEND
,CART.NAME
,CART.PMCCONTRACTSTATUSWEIGHTED
,PMCCONTRACTSTATUSLOG.[STATUS]
,ROW_NUMBER() OVER (PARTITION BY CART.CONTRACTID,CART.DATAAREAID ORDER BY DATEDIFF(second,FROMDATE,PERIODSTART) ASC) RN
FROM CUSTAMOUNTREPORTTABLE CART
LEFT JOIN PMCCONTRACTSTATUSLOG
ON PMCCONTRACTSTATUSLOG.CONTRACTID = CART.CONTRACTID
AND PMCCONTRACTSTATUSLOG.DATAAREAID = CART.DATAAREAID
AND PMCCONTRACTSTATUSLOG.FROMDATE <= CART.PERIODSTART
WHERE [STATUS] NOT IN (5,10)
)
SELECT
CONTRACTID
,ACCOUNTNUM
,AMOUNTMSTTOTAL
,PERIODSTART
,FROMDATE
,PERIODEND
,NAME
,PMCCONTRACTSTATUSWEIGHTED
,[STATUS]
FROM CTE
WHERE RN = 1
I believe you should use OUTER APPLY instead of LEFT JOIN
;WITH PMCCONTRACTSTATUS AS
(
SELECT
CONTRACTID
,[STATUS]
,FROMDATE
,DATAAREAID
,ROW_NUMBER() OVER (PARTITION BY CONTRACTID ORDER BY FROMDATE ASC) RN
FROM PMCCONTRACTSTATUSLOG
WHERE [STATUS] <> 10 OR [STATUS] <> 5
)
SELECT
CART.CONTRACTID
,PMCCONTRACTSTATUS.CONTRACTID
,CART.ACCOUNTNUM
,CART.AMOUNTMSTTOTAL
,CART.PERIODSTART
,MAX_PMCCONTRACTSTATUS.FROMDATE
,CART.PERIODEND
,CART.NAME
,CART.PMCCONTRACTSTATUSWEIGHTED
,MAX_PMCCONTRACTSTATUS.[STATUS]
FROM CUSTAMOUNTREPORTTABLE CART
OUTER APPLY (select top 1 *
from PMCCONTRACTSTATUS
where PMCCONTRACTSTATUS.CONTRACTID = CART.CONTRACTID
AND PMCCONTRACTSTATUS.DATAAREAID = CART.DATAAREAID
AND PMCCONTRACTSTATUS.FROMDATE <= CART.PERIODSTART
ORDER BY PMCCONTRACTSTATUSLOG.RN DESC) as MAX_PMCCONTRACTSTATUS

Speeding up a SQL view by pre-filtering results

I have a view operating on a very large table.
CREATE VIEW [myView]
AS
-- first part
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
GROUP BY ResultId, LocationId, TeamId)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1
GO
Now what I run operations such as
SELECT * from [MyView]
WHERE TeamId = 5
This is very slow because it is essentially executing
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
GROUP BY ResultId, LocationId, TeamId)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1 AND TeamId = 5
What I want to know is how to structure this view so it will know to filter for TeamId = 5 in the first part so that, the following code, which is fast, executes
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
GROUP BY ResultId, LocationId, TeamId
WHERE TeamId = 5)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1
You could rewrite your view as Inline TVF:
CREATE FUNCTION dbo.my_func(#TeamId INT)
RETURNS TABLE
AS
RETURN
(
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
WHERE TeamId = #TeamId OR #TeamId IS NULL
GROUP BY ResultId, LocationId, TeamId
)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1
);
And query:
SELECT *
FROM dbo.my_fun(5)
WHERE ...

How to find the maximum value in join without using if in sql stored procedure

I have a two tables like below
A
Id Name
1 a
2 b
B
Id Name
1 t
6 s
My requirement is to find the maximum id from table and display the name and id for that maximum without using case and if.
i findout the maximum by using below query
SELECT MAX(id)
FROM (SELECT id,name FROM A
UNION
SELECT id,name FROM B) as c
I findout the maximum 6 using the above query.but i can't able to find the name.I tried the below query but it's not working
SELECT MAX(id)
FROM (SELECT id,name FROM A
UNION
SELECT id,name FROM B) as c
How to find the name?
Any help will be greatly appreciated!!!
First combine the tables, since you need to search both. Next, determine the id you need. JOIN the id back with the temporarily created table to retreive the name that belongs to that id:
WITH tmpTable AS (
SELECT id,name FROM A
UNION
SELECT id,name FROM B
)
, max AS (
SELECT MAX(id) id
FROM tmpTable
)
SELECT t.id, t.name
FROM max m
JOIN tmpTable t ON m.id = t.id
You could use ROW_NUMBER(). You have to UNION ALL TableA and TableB first.
WITH TableA(Id, Name) AS(
SELECT 1, 'a' UNION ALL
SELECT 2, 'b'
)
,TableB(Id, Name) AS(
SELECT 1, 't' UNION ALL
SELECT 6, 's'
)
,Combined(Id, Name) AS(
SELECT * FROM TableA UNION ALL
SELECT * FROM TableB
)
,CTE AS(
SELECT *, RN = ROW_NUMBER() OVER(ORDER BY ID DESC) FROM Combined
)
SELECT Id, Name
FROM CTE
WHERE RN = 1
Just order by over the union and take first row:
SELECT TOP 1 * FROM (SELECT * FROM A UNION SELECT * FROM B) x
ORDER BY ID DESC
This won't show ties though.
For you stated that you used SQL Server 2008. Therefore,I used FULL JOIN and NESTED SELECT to get what your looking for. See below:
SELECT
(SELECT
1,
ISNULL(A.Id,B.Id)Id
FROM A FULL JOIN B ON A.Id=B.Id) AS Id,
(SELECT
1,
ISNULL(A.Name,B.Name)Name
FROM A FULL JOIN B ON A.Id=B.Id) AS Name
It's possible to use ROW_NUMBER() or DENSE_RANK() functions to get new numiration by Id, and then select value with newly created orderId equal to 1
Use:
ROW_NUMBER() to get only one value (even if there are some repetitions of max id)
DENSE_RANK() to get all equal max id values
Here is an example:
DECLARE #tb1 AS TABLE
(
Id INT
,[Name] NVARCHAR(255)
)
DECLARE #tb2 AS TABLE
(
Id INT
,[Name] NVARCHAR(255)
)
INSERT INTO #tb1 VALUES (1, 'A');
INSERT INTO #tb1 VALUES (7, 'B');
INSERT INTO #tb2 VALUES (4, 'C');
INSERT INTO #tb1 VALUES (7, 'D');
SELECT * FROM
(SELECT Id, Name, ROW_NUMBER() OVER (ORDER BY Id DESC) AS [orderId]
FROM
(SELECT Id, Name FROM #tb1
UNION
SELECT Id, Name FROM #tb2) as tb3) AS TB
WHERE [orderId] = 1
SELECT * FROM
(SELECT Id, Name, DENSE_RANK() OVER (ORDER BY Id DESC) AS [orderId]
FROM
(SELECT Id, Name FROM #tb1
UNION
SELECT Id, Name FROM #tb2) as tb3) AS TB
WHERE [orderId] = 1
Results are:

How to optimize the query, adding rowNumber in where clause

In a table I have more than 700,000 records. When I run this query it takes more than 3 minutes to fetch the rows, and returns 390 records based on rowNum. Is there way to optimize this query?
SELECT ID, Lat, Long, SDateTime,
row_number() OVER (partition BY [ID] ORDER BY [SDateTime] DESC) AS rowNum
into #temp
FROM
dbo.myTable WITH (NOLOCK)
select * from #temp where rowNum = 1 -- returns 390 records
drop table #temp
Can I select data in one query without putting it in temp table? like this:
SELECT ID, Lat, Long, SDateTime,
row_number() OVER (partition BY [ID] ORDER BY [SDateTime] DESC) AS rowNum
FROM
dbo.myTable WITH (NOLOCK)
where (row_number() OVER (partition BY [ID] ORDER BY [SDateTime] DESC)) = 1
try doing this !
select * from
(
SELECT ID, Lat, Long, SDateTime,
row_number() OVER (partition BY [ID] ORDER BY [SDateTime] DESC) AS rowNum
FROM
dbo.myTable WITH (NOLOCK)
)x
where x.rowNum=1
This should do the job, but it will work very well with an index on (id,SDateTime)
;with d as (
select distinct id
from myTable
)
select
mt.*
from d
cross apply (
select top 1 *
from myTable m
on m.id = d.id
order by [SDateTime] DESC
) mt

Resources