Related
I need to group the same ID into one row and keep the row containing the most data.
If an ID group have no data, I still want 1 row returned. I have roughly 30 data columns.
Example:
ID
City
Country
Data A
Data B
Data30
1
City1
Country1
DataA1
DataB1
DataN1
1
City2
Country2
Null
Null
Null
1
City3
Country3
Null
Null
Null
2
City1
Country1
DataA1
DataB1
DataN1
2
City2
Country2
Null
Null
Null
2
City3
Country3
Null
Null
Null
3
City1
Country1
Null
Null
Null
3
City2
Country2
Null
Null
Null
3
City3
Country3
Null
Null
Null
Result:
ID
City
Country
Data A
Data B
Data30
1
City1
Country1
DataA1
DataB1
DataN1
2
City1
Country1
DataA1
DataB1
DataN1
3
City1
Country1
Null
Null
Null
Any suggestion would greatly be appreciated!
This is easy to do if you sum the number of non-null columns and then apply a row_number
with cte as (
select *, Row_Number() over(partition by id order by tot desc) rn
from (
select * , Iif(data1 is null,0,1) + Iif(data2 is null,0,1) + Iif(data30 is null,0,1) tot
from t
)x
)
select id, city, country, data1, data2, data30
from cte
where rn=1
See Working demo
I'm trying to pivot data in my query (SQL Server 2008 R2) and I only need 2 columns pivoted but there may be up to 20 columns after the pivot. Here is my test data with up to 5 diagnosis codes:
pid DiagnosisCode
111 145.9
111 17.43
111 17.84
111 196.2
111 202.81
112 204.21
112 249.71
112 263.8
112 145.9
113 269.8
113 276.7
The output I'm trying to get looks like this:
pid | code1 | code2 | code3 | code4 | code5 | code6 | code... | code20
----------------------------------------------------------------------------
111 145.9 17.43 17.84 196.2 202.81 NULL NULL NULL
112 204.21 249.71 263.8 145.9 NULL NULL NULL NULL
113 269.8 276.7 NULL NULL NULL NULL NULL NULL
The code I have is :
select pid, DiagnosisCode
from
(select pid, DiagnosisCode, row_number() over(partition by pid order by pid)
as seq
from #temp
) as src
pivot
(min(DiagnosisCode)
for seq
in (DiagnosisCode)) pvt
For whatever reason, this function isn't clicking with me.I know that the MIN() aggregate function is required, but I don't need one in my output. I added the ROW_NUMBER() line so there would be a sequence field after reading one post, but I'm not sure why its needed. I've been reading all the other Pivot posts here and on other sites. I know this has been gone over many times on this site, but if you could help me understand what else I need in my query to get this to work I would be grateful.
You need to Fix your columns; the SQL Pivot table is different from Access, this is an example
declare #t as table (pid int , DiagnosisCode decimal(10,1))
insert #t select 111 , 145.9
insert #t select 111 , 17.43
insert #t select 111 , 17.84
insert #t select 111 , 196.2
insert #t select 111 , 202.81
insert #t select 112 , 204.21
insert #t select 112 , 249.71
insert #t select 112 , 263.8
insert #t select 112 , 145.9
insert #t select 113 , 269.8
insert #t select 113 , 276.7
select * from #t
select pid
,code1
,code2
,code3
,code4
,code5
,code6
,code7
,code8
,code9
,code10
,code11
,code12
,code13
,code14
,code15
,code16
,code17
,code18
,code19
,code20
from
(
select pid, DiagnosisCode, 'code' + cast(row_number() over(partition by pid order by pid) as varchar(2))
as seq
from #t
) as src
pivot
(min(DiagnosisCode)
for seq
in (
code1
,code2
,code3
,code4
,code5
,code6
,code7
,code8
,code9
,code10
,code11
,code12
,code13
,code14
,code15
,code16
,code17
,code18
,code19
,code20
)) pvt
pid code1 code2 code3 code4 code5 code6 code7 .... code20
---- ------ ------ ------- ------- ------ ------ ------ -------
111 145.9 17.4 17.8 196.2 202.8 NULL NULL NULL
112 204.2 249.7 263.8 145.9 NULL NULL NULL NULL
113 269.8 276.7 NULL NULL NULL NULL NULL NULL
I have a table in which I run a select statement and returns the following data
Name date skill seconds calls
----------------------------------------------
bob 9/2/2016 706 12771 56
bob 9/2/2016 707 4061 16
bob 9/2/2016 708 2577 15
bob 9/2/2016 709 2156 6
I want to return like below one row of data:
Name date 706sec 706call 707sec 707call 708sec 708call 709sec 709call
----------------------------------------------------------------------------------
bob 9/2/2016 12771 56 4061 16 2577 15 2156 6
My first attempt was a pivot but does not return a single row:
Select
name, date, seconds, calls, [706], [707],[708],[709],
from
(Select
name, date, skill, seconds, calls
From
tablecalls
Where
date between '09/02/2016 00:00' and '09/02/2016 23:59'
and name = 'bob') as b
pivot
(sum(seconds) for skill in ([706], [707], [708], [709] )) as p1
This returns:
name date calls 706sec 707sec 708sec 709sec
---------------------------------------------------------
bob 9/2/2016 6 NULL NULL NULL 2156
bob 9/2/2016 15 NULL NULL 2577 NULL
bob 9/2/2016 16 NULL 4061 NULL NULL
bob 9/2/2016 56 12771 NULL NULL NULL
Maybe PIVOT is not the right way to do this. Is there another way?
can you format your data in question. I am not sure whether I get youe point?
CREATE TABLE #tt([name] VARCHAR(10),[date] DATE,skill INT,seconds INT, calls int )
INSERT INTO #tt
SELECT 'bob','9/2/2016',706,12771,56 UNION all
SELECT 'bob','9/2/2016',707,4061,16 UNION all
SELECT 'bob','9/2/2016',708,2577,15 UNION all
SELECT 'bob','9/2/2016',709,2156,6
SELECT * FROM (
SELECT t.name,t.date,c.* FROM #tt AS t
CROSS APPLY(VALUES(LTRIM(t.skill)+'sec',t.seconds),(LTRIM(t.skill)+'calls',t.seconds)) c(t,v)
) AS A
PIVOT(MAX(v) FOR t IN ([706sec],[706calls],[707sec],[707calls],[708sec],[708calls],[709sec],[709calls])) p
name date 706sec 706calls 707sec 707calls 708sec 708calls 709sec 709calls
---------- ---------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
bob 2016-09-02 12771 12771 4061 4061 2577 2577 2156 2156
if the count of skill is not fix, you can use dynamic script:
DECLARE #col VARCHAR(max),#sql VARCHAR(max)
SELECT #col=ISNULL(#col+',[','[')+LTRIM(skill)+'sec],['+LTRIM(skill)+'calls]' FROM #tt GROUP BY skill
SET #sql='
SELECT * FROM (
SELECT t.name,t.date,c.* FROM #tt AS t
CROSS APPLY(VALUES(LTRIM(t.skill)+''sec'',t.seconds),(LTRIM(t.skill)+''calls'',t.seconds)) c(t,v)
) AS A
PIVOT(MAX(v) FOR t IN ('+#col+')) p'
EXEC (#sql)
I have a table with data like so:
Employee PRDate Type Code Amount Subject Eligible
1234 1/1/2015 D 1 100.00 100.00 0.00
1234 1/1/2015 D 2 200.00 0.00 0.00
5678 1/1/2015 D 1 500.00 40.00 500.00
1234 1/1/2015 E 1 300.00 30.00 300.00
5678 1/1/2015 E 1 700.00 700.00 500.00
1234 1/1/2015 E 2 400.00 200.00 0.00
1234 1/8/2015 L 55 40.00 40.00 40.00
And I need for the data to be displayed like this:
Employee PRDate D1Amt D1Subj D1Elig D2Amt D2Subj D2Elig E1Amt E1Subj E1Elig E2Amt E2Subj E2Elig L55Amt L55Subj L55Elig
1234 1/1/2015 100.00 100.00 0.00 200.00 0.00 0.00 300.00 30.00 300.00 400.00 200.00 0.00 40.00 40.00 40.00
4678 1/1/2015 500.00 40.00 500.00 700.00 700.00 500.00
I can pivot on one column but when I try combining the Type and Code columns to get the one I get conversion errors (Type is a varchar and code is a tinyint). I'm not sure how to get to the desired results other than dynamic pivot. Can the desired results be achieved?
I've gotten this far but I can't figure out how to combine the type, code and each money columns (amount, subject and eligible) to get the data under the correct columns.
IF EXISTS (
SELECT *
FROM sys.tables
WHERE name LIKE '#temp285865%')
DROP TABLE #temp285865;
Create table dbo.#temp285865
(
EDLCodetemp varchar(10)
);
INSERT INTO #temp285865
(
[EDLCodetemp]
)
SELECT DISTINCT EDLCode
FROM #results
ORDER BY EDLCode;
-- Building a comma separated list of EDLCodes in #edltemp
DECLARE #cols varchar(1000);
SELECT #cols = COALESCE(#cols + ',[' + [EDLCodetemp] + ']', '[' + [EDLCodetemp] + ']')
FROM #temp285865;
-- Building the query appending columns
DECLARE #query varchar(4000);
SET #query =
'SELECT [CoName],
[PRCo],
[PRGroup],
[PREndDate],
[PaySeq],
[EDLType],
[Hours],
[SubjectAmt],
[EligibleAmt],
[PaidMth],
[LastName],
[FirstName],
[UseOver],
[OverAmt],
[Amount],
[PRGRDescrip],
[LimitPeriod],
[LimitMth],
[PREHEmployee],
[SortName],
[PaybackAmt],
[PaybackOverAmt],
[PaybackOverYN],
[PRDTEmployee],
[TrueEarns], '
+ #cols + ' FROM
(
SELECT [CoName],
[PRCo],
[PRGroup],
[PREndDate],
[PaySeq],
[EDLType],
[Hours],
[SubjectAmt],
[EligibleAmt],
[PaidMth],
[LastName],
[FirstName],
[PRDLDescrip],
[PRECDescrip],
[UseOver],
[OverAmt],
[Amount],
[PRGRDescrip],
[LimitPeriod],
[LimitMth],
[PREHEmployee],
[SortName],
[PaybackAmt],
[PaybackOverAmt],
[PaybackOverYN],
[PRDTEmployee],
[TrueEarns],
[EDLCode]
FROM #results
) p
PIVOT (
MAX(EDLCode)
FOR [EDLCode] IN (' + #cols + ')
)
as pvt';
EXEC(#query);
DROP TABLE #temp285865;
The following PIVOT with dynamic SQL would give you the result you want, based on the input data you provided (I changed the PRDate in the last row though).
The first statement builds an intermediate table #bt with the column names you want and the associated value. Then the column names are built in #cols for the dynamic SQL statement. Finally the intermediate table #bt is pivoted with a dynamic SQL statement using the #cols to pivot.
SET NOCOUNT ON;
CREATE TABLE #t(
Employee INT,
PRDate DATETIME,
Type CHAR(1),
Code TINYINT,
Amount DECIMAL(28,2),
Subject DECIMAL(28,2),
Eligible DECIMAL(28,2)
);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','D',1,100.00,100.00,0.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','D',2,200.00,0.00,0.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(5678,'2015-01-01','D',1,500.00,40.00,500.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','E',1,300.00,30.00,300.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(5678,'2015-01-01','E',1,700.00,700.00,500.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','E',2,400.00,200.00,0.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','L',55,40.00,40.00,40.00);
SELECT
Employee,
PRDate,
Type+CAST(Code AS VARCHAR(3))+ca.name AS colname,
ca.val
INTO
#bt
FROM
#t
CROSS APPLY(
SELECT Amount AS val,'Amt' AS name
UNION ALL
SELECT Subject AS val,'Subj' AS name
UNION ALL
SELECT Eligible AS val,'Elig' AS name
) AS ca;
/* If you need to SUM for all dates, instead use this statement to create #bt
SELECT
Employee,
Type+CAST(Code AS VARCHAR(3))+ca.name AS colname,
ca.val
INTO
#bt
FROM
(
SELECT
Employee,
Type,
Code,
SUM(Amount) AS Amount,
SUM(Subject) AS Subject,
SUM(Eligible) AS Eligible
FROM
#t
GROUP BY
Employee,
Type,
Code
) AS t
CROSS APPLY(
SELECT Amount AS val,'Amt' AS name
UNION ALL
SELECT Subject AS val,'Subj' AS name
UNION ALL
SELECT Eligible AS val,'Elig' AS name
) AS ca;
*/
DECLARE #cols VARCHAR(8000);
SET #cols=STUFF(
(SELECT DISTINCT
',['+colname+']'
FROM
#bt
FOR XML PATH('')),
1,
1,
''
);
DECLARE #sql VARCHAR(MAX);
SET #sql='
SELECT
*
FROM
#bt
PIVOT(
MAX(val)
FOR colname IN ('+#cols+')
) AS piv
';
EXEC (#sql);
DROP TABLE #bt;
DROP TABLE #t;
The result is the following:
Employee PRDate D1Amt D1Elig D1Subj D2Amt D2Elig D2Subj E1Amt E1Elig E1Subj E2Amt E2Elig E2Subj L55Amt L55Elig L55Subj
1234 2015-01-01 100.00 0.00 100.00 200.00 0.00 0.00 300.00 300.00 30.00 400.00 0.00 200.00 40.00 40.00 40.00
5678 2015-01-01 500.00 500.00 40.00 NULL NULL NULL 700.00 500.00 700.00 NULL NULL NULL NULL NULL NULL
I have a SQL table which stores the Status and Location of employees
empID date status location
---------------------------------------------------------
001 01.01.2014 1 1
001 02.01.2014 2 1
001 04.01.2014 1 3
....
055 01.01.2014 3 3
055 02.01.2014 4 2
Now I want to create a list with the employee id and days 1 to 31 as columns
and a concatenation of the Status and Location as the value. If I take max() of the status, it works, however, the location information is missing. If I try to do
SELECT *
FROM (SELECT (empId), left(datename(dd,[Date]),2)as [day_date], [Status] as status, [Location] as location, [Status] + '|' + [Location] as val FROM [dbo].[table]
WHERE [date] BETWEEN '2014-01-01' AND '2014-01-30') as s
PIVOT (MAX(val) FOR [day_date] IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31])
)AS p
order by empID
it fails because I can't use max() against a concatenated string.
My goal is:
empID 01 02 03 04 .... 30 31
---------------------------------------------------------
001 1|1 2|1 null 1|3 null null
055 3|4 4|2 null null null null
Cast [Status] and [Location] to a varchar performing the concatenation. I have arbitrarily chosen 3 as the length of the varchar but that may vary depending on what your maximum possible [Status] and [Location] are.
Also, remove the [Status] and [Location] columns from the inner query.
SELECT *
FROM (
SELECT
empId,
left(datename(dd,[Date]),2)as [day_date],
cast([Status] AS varchar(3)) + '|' + cast([Location] as varchar(3)) as val
FROM [dbo].[table]
WHERE [date] BETWEEN '2014-01-01' AND '2014-01-30') as s
PIVOT (MAX(val) FOR [day_date] IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31])
)AS p
order by empID