T-SQL for "Not quite a Pivot" - sql-server

I have the following source data (the data is an extract for a source of several hundred rows.):
ID CodeID Code
3749 69 354
3750 69 864
33721 130 XXX
33722 130 319
30446 159 XXX
30447 159 XXX
and using T-SQL I need to achieve:
CodeID Code1 Code2
69 354 864
130 XXX 319
159 XXX XXX
This doesn't seem to fit the structure for a pivot table and I have no idea how to achieve this. Does anyone have any suggestions.

You can do it with a pivot if you first assign each of the values a number using row_number()
select codeid, [1] as Code1,[2] as Code2 -- .... ,[3] etc
from
(
select codeid, code, ROW_NUMBER() over (partition by codeid order by id) rn
from yourtable
) p
pivot (max(code) for rn in ([1],[2])) p2 --, [3]... etc

Related

Transpose and Create Variables

I have data that looks like:
PT_ID Time HR RR SysBP
1 830 120 24
1 900 124 118
1 1000 115
1 1045 118 20
ETC.
Desired output:
PT_ID Time1 HR1 RR1 SysBP1 Time2 HR2 RR2 SysBp2 Time3 Etc
1 830 24 900 124 118 1000
This type of manipulation is too tricky for my novice knowledge of SQL and was curious if you all know of a potential solution.
If you have an known, or maximum number of observations, you can use a PIVOT in concert with Row_Number() (assuming TIME is the proper sequence)
If the number of observations/columns is not known, then you'll have to use some dynamic SQL.
Example
Select *
From (
Select A.[PT_ID]
,B.*
From (Select *,RN=Row_Number() over (Partition By PT_ID Order by Time) From YourTable ) A
Cross Apply (values (concat('Time' ,RN),[Time])
,(concat('HR' ,RN),[HR])
,(concat('RR' ,RN),[RR])
,(concat('SysBP',RN),[SysBP])
) B(Item,Value)
) src
Pivot (sum(Value) for Item in ([Time1],[HR1],[RR1],[SysBP1]
,[Time2],[HR2],[RR2],[SysBP2]
,[Time3],[HR3],[RR3],[SysBP3]
,[Time4],[HR4],[RR4],[SysBP4]
) ) pvt
Returns
I accidentally edited this post and did not mean to, deleting the edit.

AVG of a resultset

Good morning. SQL new guy here seeking some help. I'm attempting to get the AVG of my resultset from a subquery. The subquery works just fine and gives me the resultset I need, but I just cannot get the AVG of the resultset to work. Any guidance would be greatly appreciated.
SELECT AVG(COUNT) FROM
(SELECT COUNT(DISTINCT(table2.item_no))
FROM table1
JOIN table2 ON table1.order_numb = table2.order_numb
WHERE user_so = 'paul'
AND order_date BETWEEN '9/20/2017'AND '9/20/2018'
GROUP BY table1.order_numb);
Here is a sample of the resultset from the subquery on its own that I'm trying to then turn around and get an AVG of:
216
181
163
156
144
144
143
133
129
129
120
114
113
112
112
109
108
104
103
99
98
98
98
98
98
97
97
97
96
96
94
94
94
93
93
I think you want something like this:
SELECT AVG(a_number) FROM
(SELECT COUNT(DISTINCT(table2.item_no)) AS a_number
FROM table1
JOIN table2 ON table1.order_numb = table2.order_numb
WHERE user_so = 'paul'
AND order_date BETWEEN '9/20/2017'AND '9/20/2018'
GROUP BY table1.order_numb) AS the_subquery
I don't have the same tables with data, so here is a cut down working example:
SELECT AVG(a_number) FROM (
SELECT 100 AS a_number
UNION
SELECT 200 AS a_number
UNION
SELECT 300 AS a_number
UNION
SELECT 400 AS a_number
) AS the_subquery
It looks like you were missing that you need to alias the subquery and you want to AVG the alias of the value being returned in the subquery.
update
As per the comment, if you'd like the answer to be rounded to 2 decimal points you will need to CAST it to a different data type, like this:
SELECT ROUND(AVG(CAST(a_number AS FLOAT)), 2) FROM
(SELECT COUNT(DISTINCT(table2.item_no)) AS a_number
FROM table1
JOIN table2 ON table1.order_numb = table2.order_numb
WHERE user_so = 'paul'
AND order_date BETWEEN '9/20/2017'AND '9/20/2018'
GROUP BY table1.order_numb) AS the_subquery
or for others without access to the table:
SELECT ROUND(AVG(CAST(a_number AS FLOAT)), 2) FROM (
SELECT 100 AS a_number
UNION
SELECT 200 AS a_number
UNION
SELECT 300 AS a_number
UNION
SELECT 403 AS a_number
) AS the_subquery
You can read more about how ROUND, AVG, and CAST work here: How do I retrieve decimals when rounding an average in SQL

T-SQL Count of Records in Status for Previous Months

I have a T-SQL Quotes table and need to be able to count how many quotes were in an open status during past months.
The dates I have to work with are an 'Add_Date' timestamp and an 'Update_Date' timestamp. Once a quote is put into a 'Won' or 'Loss' columns with a value of '1' in that column it can no longer be updated. Therefore, the 'Update_Date' effectively becomes the Closed_Status timestamp.
Here's a few example records:
Quote_No Add_Date Update_Date Open_Quote Win Loss
001 01-01-2016 NULL 1 0 0
002 01-01-2016 3-1-2016 0 1 0
003 01-01-2016 4-1-2016 0 0 1
Here's a link to all the data here:
https://drive.google.com/open?id=0B4xdnV0LFZI1T3IxQ2ZKRDhNd1k
I asked this question previously this year and have been using the following code:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
)
select format(dateadd(month, n.n, q.add_date), 'yyyy-MM') as yyyymm,
count(*) as Open_Quote_Count
from quotes q join
n
on (closed_status = 1 and dateadd(month, n.n, q.add_date) <= q.update_date) or
(closed_status = 0 and dateadd(month, n.n, q.add_date) <= getdate())
group by format(dateadd(month, n.n, q.add_date), 'yyyy-MM')
order by yyyymm;
The problem is this code is returning a cumulative value. So January was fine, but then Feb is really Jan + Feb, and March is Jan+Feb+March, etc. etc. It took me a while to discover this and the numbers returned now way, way off and I'm trying to correct them.
From the full data set the results of this code are:
Year-Month Open_Quote_Count
2017-01 153
2017-02 265
2017-03 375
2017-04 446
2017-05 496
2017-06 560
2017-07 609
The desired result would be how many quotes were in an open status during that particular month, not the cumulative :
Year-Month Open_Quote_Count
2017-01 153
2017-02 112
2017-03 110
2017-04 71
Thank you in advance for your help!
Unless I am missing something, LAG() would be a good fit here
Example
Declare #YourTable Table ([Year-Month] varchar(50),[Open_Quote_Count] int)
Insert Into #YourTable Values
('2017-01',153)
,('2017-02',265)
,('2017-03',375)
,('2017-04',446)
,('2017-05',496)
,('2017-06',560)
,('2017-07',609)
Select *
,NewValue = [Open_Quote_Count] - lag([Open_Quote_Count],1,0) over (Order by [Year-Month])
From #YourTable --<< Replace with your initial query
Returns
Year-Month Open_Quote_Count NewValue
2017-01 153 153
2017-02 265 112
2017-03 375 110
2017-04 446 71
2017-05 496 50
2017-06 560 64
2017-07 609 49

How to select specific records of groups based on criteria

I'm trying to group a set of data and for some of the fields I need to select a specific value based on the ttype, for example I have the following rows:
caseid age iss gcs ttype
00170 64 25 17 Transfer Out
00170 64 27 15 Transfer In
00201 24 14 40 Transfer In
If a caseID has ttype 'Transfer Out' I want to use the ISS and GCS values from this row, otherwise use the values from the 'Transfer In' row.
My desired output based on the above example would be:
caseid age iss gcs
00170 64 25 17
00201 24 14 40
My current select statement is:
select caseid, max(age), max(iss), max(gcs)
from Table1
group by caseid
Which I know is incorrect but how do I specify the values for ISS and GCS from a specific row?
Thanks
Edit - I will not always need to select from Row1, table below with expanded data:
caseid age iss gcs los ttype disdate
170 64 25 17 5 Transfer Out 2014-01-02 00:00:00.000
170 64 27 15 1 Transfer In 2014-01-04 00:00:00.000
201 24 14 40 4 Transfer In 2014-01-04 00:00:00.000
In this case, I want the max age and the ISS and GCS figure for row1 as before but I need to sum the LOS and select the disdate for row 2 (ie the latest date), so my output would be:
caseid age iss gcs los disdate
170 64 25 17 6 2014-01-04
201 24 14 40 4 2014-01-04
Is this possible?
You can use a CTE and ROW_NUMBER + Over-clause (edited acc. to your updated question):
WITH CTE AS
(
SELECT caseid, age, iss, gcs, los, ttype, disdate,
SumLos = SUM(los) OVER (PARTITION BY caseid),
LatestDisDate = MAX(disdate) OVER (PARTITION BY caseid),
rn = ROW_NUMBER() OVER (PARTITION BY caseid
ORDER BY CASE WHEN ttype = 'Transfer Out'
THEN 0 ELSE 1 END ASC, disdate ASC)
FROM dbo.Table1
)
SELECT caseid, age, iss, gcs, los = SumLos, disdate = LatestDisDate
FROM CTE
WHERE rn = 1
Demo
I think this is what you need -
;WITH CTE AS
(
SELECT case_id, age,iss,gcs, ROW_NUMBER () over (PARTITION BY ttype order by gcs DESC) Rn
from YOUR_TABLE_NAME
)
SELECT case_id,age,iss,gcs
from CTE where Rn =1

Column to comma seperated value partition by ID

i have on sql server 2008 table like
EmployeeCertificationHistoryId EmployeeCertificationID EmployeeID CertificationID CertificationDate
1 244 2192 1 2/15/2006
2 185 2058 87 4/10/2010
3 245 2240 102 8/11/2013
4 246 2249 104 11/23/2005
5 247 2221 101 6/12/2013
6 248 2238 84 NULL
7 245 2240 102 8/11/2013
8 249 2240 102 8/4/2013
10 253 2175 84 6/19/2013
11 254 2239 105 2/5/2011
12 255 2239 111 11/22/2012
9 96 1468 92 12/6/2010
13 256 2239 110 11/22/2012
i need to comma seperate certificationid per employeeid.
for eg. for 2239=>105,111,110
i have written a query but it is giving all certificate id in one column. my query is
SELECT STUFF(
(SELECT ',' + CAST(C.CertificationID AS VARCHAR(100))
FROM tbl_PM_EmployeeCertificationMatrixHistory C
ORDER BY c.CertificationID
FOR XML PATH('')),1,1,'') AS CSV
GO
i just need employeeid and certificationid.but i am unable to sort it out.
You need a correlated subquery and a list of employees. The following gets the list of employees from the same table but you might have another table with this information:
SELECT e.EmployeeID,
STUFF((SELECT ',' + CAST(C.CertificationID AS VARCHAR(100))
FROM tbl_PM_EmployeeCertificationMatrixHistory C
where c.EmployeeID = e.EmployeeID
ORDER BY c.CertificationID
FOR XML PATH('')
),1, 1,'') AS CSV
from (select distinct EmployeeID
from tbl_PM_EmployeeCertificationMatrixHistory
) e;
You just need to add EmployeeID to the query as well as a WHERE and DISTINCT
SELECT DISTINCT A.EmployeeID, STUFF(
(SELECT ',' + CAST(C.CertificationID AS VARCHAR(100))
FROM tbl_PM_EmployeeCertificationMatrixHistory C
WHERE C.EmployeeID = A.EmployeeID
ORDER BY c.CertificationID
FOR XML PATH('')),1,1,'') AS CSV
FROM tbl_PM_EmployeeCertificationMatrixHistory A
GO
If you want to return only DISTINCT values in the the CSV list, add GROUP BY c.CertificationID above the ORDER BY

Resources