Transpose and Create Variables - sql-server

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.

Related

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 would you write a T-SQL query that supported event study analysis

I trying to create a table that will support a simple event study analysis, but I'm not sure how best to approach this.
I'd like to create a table with the following columns: Customer, Date, Time on website, Outcome. I'm testing the premise that the outcome for a particular customer on any give day if a function of the time spent on the website on the current day as well as the preceding five site visits. I'm envisioning a table similar to this:
I'm hoping to write a T-SQL query that will produce an output like this:
Given this objective, here are my questions:
Assuming this is indeed possible, how should I structure my table to accomplish this objective? Is there a need for a column that refers to the prior visit? Do I need to add an index to a particular column?
Would this be considered a recursive query?
Given the appropriate table structure, what would the query look like?
Is it possible to structure the query with a variable that determines the number of prior periods to include in addition to the current period (for example, if I want to compare 5 periods to 3 periods)?
Not sure I understand analytic value of your matrix
Declare #Table table (id int,VisitDate date,VisitTime int,Outcome varchar(25))
Insert Into #Table (id,VisitDate,VisitTime,Outcome) values
(123,'2015-12-01',100,'P'),
(123,'2016-01-01',101,'P'),
(123,'2016-02-01',102,'N'),
(123,'2016-03-01',100,'P'),
(123,'2016-04-01', 99,'N'),
(123,'2016-04-09', 98,'P'),
(123,'2016-05-09', 99,'P'),
(123,'2016-05-14',100,'N'),
(123,'2016-06-13', 99,'P'),
(123,'2016-06-15', 98,'P')
Select *
,T0 = VisitTime
,T1 = Lead(VisitTime,1,0) over(Partition By ID Order By ID,VisitDate Desc)
,T2 = Lead(VisitTime,2,0) over(Partition By ID Order By ID,VisitDate Desc)
,T3 = Lead(VisitTime,3,0) over(Partition By ID Order By ID,VisitDate Desc)
,T4 = Lead(VisitTime,4,0) over(Partition By ID Order By ID,VisitDate Desc)
,T5 = Lead(VisitTime,5,0) over(Partition By ID Order By ID,VisitDate Desc)
From #Table
Order By ID,VisitDate Desc
Returns
id VisitDate VisitTime Outcome T0 T1 T2 T3 T4 T5
123 2016-06-15 98 P 98 99 100 99 98 99
123 2016-06-13 99 P 99 100 99 98 99 100
123 2016-05-14 100 N 100 99 98 99 100 102
123 2016-05-09 99 P 99 98 99 100 102 101
123 2016-04-09 98 P 98 99 100 102 101 100
123 2016-04-01 99 N 99 100 102 101 100 0
123 2016-03-01 100 P 100 102 101 100 0 0
123 2016-02-01 102 N 102 101 100 0 0 0
123 2016-01-01 101 P 101 100 0 0 0 0
123 2015-12-01 100 P 100 0 0 0 0 0
With fixed columns you can do it like this with lag:
select
time,
lag(time, 1) over (partition by customer order by date desc),
lag(time, 2) over (partition by customer order by date desc),
lag(time, 3) over (partition by customer order by date desc),
lag(time, 4) over (partition by customer order by date desc)
from
yourtable
If you need dynamic columns, then you'll have to build it using dynamic SQL.

Pivot query without knowing the number of columns needed

I have a query that returns the following data.
ProductCode DealRef
1120 23
1120 76
1130 24
Is there a way that if a product code has more than one Deal ref then it will put this into a new column? So the current result would look something like;
ProductCode Deal1 Deal2
1120 23 76
1130 24
If this is not possible then I have an idea that could work. I would do a count on the DealRef column to find out many columns i would need to pivot to. I would then need to add another column to my initial query which will be able to add an id to each row displaying something similar to the below which I'm unsure how to do.
ProductCode DealRef id
1120 23 1
1120 76 2
1130 24 1
You cannot get the fitting number of columns, but you can get as many columns as you expect to be the maximum, most of them beeing NULL:
Paste this into an empty query window and execute. Adapt to your needs
DECLARE #tbl TABLE(ProductCode INT, DealRef INT);
INSERT INTO #tbl VALUES
(1120,23)
,(1120,76)
,(1130,24);
SELECT p.*
FROM
(
SELECT 'deal' + CAST(ROW_NUMBER() OVER(PARTITION BY tbl.ProductCode ORDER BY tbl.ProductCode) AS VARCHAR(10)) AS ColumnName
,tbl.ProductCode
,tbl.DealRef
FROM #tbl AS tbl
) AS x
PIVOT
(
MIN(DealRef) FOR ColumnName IN(deal1,deal2,deal3,deal4 /*Add as many Col-names as you could maximum need*/)
) AS p
Result is
ProductCode deal1 deal2 deal3 deal4
1120 23 76 NULL NULL
1130 24 NULL NULL NULL

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

T-SQL for "Not quite a Pivot"

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

Resources