Convert row to column using sql server 2008? - sql
Table name is Looupvalue
id Ptypefield Value
1 1 D
2 1 E
3 1 F
4 1 G
5 1 H
6 2 FL
7 2 IF
8 2 VVS1
9 2 VVS2
10 2 VS1
11 2 VS2
12 3 0.50
13 3 1.00
14 3 1.50
15 3 2.00
16 4 Marquise
17 4 Round
18 4 Pear
19 4 Radiant
20 4 Princess
Lookupvalue table value convert roow to column depends on ptypefield
Like
id 1 id 2 id 3 id 4
1 D 6 fl 12 0.50 16 Marquise
2 E 7 If 13 1 17 Round....
3 F 8 vvs2 14 1.5
4 G 9 vvs2 15 2
5 H 10 vs1
11 vs2
Thanks
In your sample output, it is not clear why values from columns 1 and 2 would be related to columns 3 and 4. However, here is a possible solution:
;With RowNumbers As
(
Select Id, PTypeField, Value
, Row_Number() Over( Partition By PTypeField Order By Id ) As Rownum
From #Test
)
Select RowNum
, Min( Case When PTypeField = 1 Then Id End ) As Id
, Min( Case When PTypeField = 1 Then Value End ) As [1]
, Min( Case When PTypeField = 2 Then Id End ) As Id
, Min( Case When PTypeField = 2 Then Value End ) As [2]
, Min( Case When PTypeField = 3 Then Id End ) As Id
, Min( Case When PTypeField = 3 Then Value End ) As [3]
, Min( Case When PTypeField = 4 Then Id End ) As Id
, Min( Case When PTypeField = 4 Then Value End ) As [4]
From RowNumbers
Group By RowNum
If you wanted to dynamically generate the columns, the only way to do that in SQL is to use some fugly dynamic SQL. T-SQL was not designed for this sort of output and instead you should use a reporting tool or do the crosstabbing in a middle tier component or class.
This data schema looks like an EAV which would explain why retrieving the data you want is so difficult.
Related
row_number() but only increment value after a specific value in a column
Query: SELECT (row_number() OVER ()) as grp, * from tbl Edit: the rows below are returned by a pgrouting shortest path function and it does have a sequence. seq grp id 1 1 8 2 2 3 3 3 2 4 4 null 5 5 324 6 6 82 7 7 89 8 8 null 9 9 1 10 10 2 11 11 90 12 12 null How do I make it so that the grp column is only incremented after a null value on id - and also keep the same order of rows seq grp id 1 1 8 2 1 3 3 1 2 4 1 null 5 2 324 6 2 82 7 2 89 8 2 null 9 3 1 10 3 2 11 3 90 12 3 null
demo:db<>fiddle Using a cumulative SUM aggregation is a possible approach: SELECT SUM( -- 2 CASE WHEN id IS NULL THEN 1 ELSE 0 END -- 1 ) OVER (ORDER BY seq) as grp, id FROM mytable If the current (ordered!) value is NULL, then make it 1, else 0. Now you got a bunch of zeros, delimited by a 1 at each NULL record. If you'd summerize these values cumulatively, at each NULL record, the sum increased. Execution of the cumulative SUM() using window functions This yields: 0 8 0 3 0 2 1 null 1 324 1 82 1 89 2 null 2 1 2 2 2 90 3 null As you can see, the groups start with the NULL records, but you are expecting to end it. This can be achieved by adding another window function: LAG(), which moves the records to the next row: SELECT SUM( CASE WHEN next_id IS NULL THEN 1 ELSE 0 END ) OVER (ORDER BY seq) as grp, id FROM ( SELECT LAG(id) OVER (ORDER BY seq) as next_id, seq, id FROM mytable ) s The result is your expected one: 1 8 1 3 1 2 1 null 2 324 2 82 2 89 2 null 3 1 3 2 3 90 3 null
SQL: subset data: select id when time_id for id satisfy a condition from another column
I have a data (dt) in SQL like the following: ID time_id act rd 11 1 1 1 11 2 4 1 11 3 7 0 12 1 8 1 12 2 2 0 12 3 4 1 12 4 3 1 12 5 4 1 13 1 4 1 13 2 1 0 15 1 3 1 16 1 8 0 16 2 8 0 16 3 8 0 16 4 8 0 16 5 8 0 and I want to take the subset of this data such that only ids (and their corresponding time_id, act, rd) that has time_id == 5 is retained. The desired output is the following ID time_id act rd 12 1 8 1 12 2 2 0 12 3 4 1 12 4 3 1 12 5 4 1 16 1 8 0 16 2 8 0 16 3 8 0 16 4 8 0 16 5 8 0 I know I should use having clause somehow but have not been successful so far (returns me empty outputs). below is my attempt: SELECT * FROM dt GROUP BY ID Having min(time_id) == 5;
This query: select id from tablename where time_id = 5 returns all the ids that you want in the results. Use it with the operator IN: select * from tablename where id in (select id from tablename where time_id = 5)
You can use a correlated subquery with exists: select t.* from t where exists (select 1 from t t2 where t2.id = t.id and t2.time_id = 5);
WITH temp AS ( SELECT id FROM tab WHERE time_id = 5 ) SELECT * FROM tab t join temp tp on(t.id=tp.id);
check this query select * from table t1 join (select distinct ID from table t where time_id = 5) t2 on t1.id =t2.id;
Select top rows until value in specific column has appeared twice
I have the following query where I am trying to select all records, ordered by date, until the second time EmailApproved = 1 is found. The second record where EmailApproved = 1 should not be selected. declare #Test table (id int, EmailApproved bit, Created datetime); insert into #Test (id, EmailApproved, Created) values (1,0,'2011-03-07 03:58:58.423') , (2,0,'2011-02-21 04:55:52.103') , (3,0,'2011-01-29 13:24:02.103') , (4,1,'2010-10-12 14:41:54.217') , (5,0,'2010-10-12 14:34:15.903') , (6,0,'2010-10-12 10:10:19.123') , (7,1,'2010-08-27 12:07:16.073') , (8,1,'2010-08-25 12:15:49.413') , (9,0,'2010-08-25 12:14:51.970') , (10,1,'2010-04-12 16:43:44.777'); select * , case when Row1 = Row2 then 1 else 0 end Row1EqualRow2 from ( select id, EmailApproved, Created , row_number() over (partition by EmailApproved order by Created desc) Row1 , row_number() over (order by Created desc) Row2 from #Test ) X --where Row1 = Row2 order by Created desc; Which produces the following results: id EmailApproved Created Row1 Row2 Row1EqualsRow2 1 0 2011-03-07 03:58:58.423 1 1 1 2 0 2011-02-21 04:55:52.103 2 2 1 3 0 2011-01-29 13:24:02.103 3 3 1 4 1 2010-10-12 14:41:54.217 1 4 0 5 0 2010-10-12 14:34:15.903 4 5 0 6 0 2010-10-12 10:10:19.123 5 6 0 7 1 2010-08-27 12:07:16.073 2 7 0 8 1 2010-08-25 12:15:49.413 3 8 0 9 0 2010-08-25 12:14:51.970 6 9 0 10 1 2010-04-12 16:43:44.777 4 10 0 What I actually want is: id EmailApproved Created Row1 Row2 Row1EqualsRow2 1 0 2011-03-07 03:58:58.423 1 1 1 2 0 2011-02-21 04:55:52.103 2 2 1 3 0 2011-01-29 13:24:02.103 3 3 1 4 1 2010-10-12 14:41:54.217 1 4 0 5 0 2010-10-12 14:34:15.903 4 5 0 6 0 2010-10-12 10:10:19.123 5 6 0 Note: Row, Row2 & Row1EqualsRow2 are just working columns to show my calculations.
Steps: Create a row number, rn, over all rows in case id is not in sequence. Create a row number, approv_rn, partitioned by EmailApproved so we know when EmailApproved = 1 for the second time Use a outer apply to find the row number of the second instance of EmailApproved = 1 In the where clause filter out all rows where the row number is >= the value found in step 3. If there is 1 or 0 EmailApproved records available then the outer apply will return null, in which case return all available rows. with test as ( select *, rn = row_number() over (order by Created desc), approv_rn = row_number() over (partition by EmailApproved order by Created desc) from #Test ) select * from test t outer apply ( select x.rn from test x where x.EmailApproved = 1 and x.approv_rn = 2 ) x where t.rn < x.rn or x.rn is null order by t.Created desc;
SQL Server - group and number matching contiguous values
I have a list of stock transactions and I am using Over(Partition By) to calculate the running totals (positions) by security. Over time a holding in a particular security can be long, short or flat. I am trying to find an efficient way to extract only the transactions relating to the current position for each security. I have created a simplified sqlfiddle to show what I have so far. The cte query generates the running total for each security (code_id) and identifies when the holdings are long (L), short (s) or flat (f). What I need is to group and number matching contiguous values of L, S or F for each code_id. What I have so far is this: ; WITH RunningTotals as ( SELECT *, RunningTotal = sum(qty) OVER (Partition By code_id Order By id) FROM TradeData ), LongShortFlat as ( SELECT *, LSF = CASE WHEN RunningTotal > 0 THEN 'L' WHEN RunningTotal < 0 THEN 'S' ELSE 'F' END FROM RunningTotals ) SELECT * FROM LongShortFlat r I think what I need to do is create a GroupNum column by applying a row_number for each group of L, S and F within each code_id so the results look like this: id code_id qty RunningTotal LSF GroupNum 1 1 5 5 L 1 2 1 2 7 L 1 3 1 7 14 L 1 4 1 -3 11 L 1 5 1 -5 6 L 1 6 1 -6 0 F 2 7 1 5 5 L 3 8 1 5 10 L 3 9 1 -2 8 L 3 10 1 -4 4 L 3 11 2 5 5 L 1 12 2 3 8 L 1 13 2 -4 4 L 1 14 2 -2 2 L 1 15 2 -2 0 F 2 16 2 6 6 L 3 17 2 -5 1 L 3 18 2 -5 -4 S 4 19 2 2 -2 S 4 20 2 4 2 L 5 21 2 -5 -3 S 6 22 2 -2 -5 S 6 23 3 5 5 L 1 24 3 2 7 L 1 25 3 1 8 L 1 I am struggling to generate the GroupNum column. Thanks in advance for your help.
[Revised] Sorry about that, I read your question too quickly. I came up with a solution using a recursive common table expression (below), then saw that you've worked out a solution using LAG. I'll post my revised query anyway, for posterity. Either way, the resulting query is (imho) pretty ugly. ;WITH cteBaseAgg as ( -- Build the "sum increases over time" data SELECT row_number() over (partition by td.code_id order by td.code_id, td.Id) RecurseKey ,td.code_id ,td.id ,td.qty ,sum(tdPrior.qty) RunningTotal ,case when sum(tdPrior.qty) > 0 then 'L' when sum(tdPrior.qty) < 0 then 'S' else 'F' end LSF from dbo.TradeData td inner join dbo.TradeData tdPrior on tdPrior.code_id = td.code_id -- All for this code_id and tdPrior.id <= td.Id -- For this and any prior Ids group by td.code_id ,td.id ,td.qty ) ,cteRecurse as ( -- "Set" the first row for each code_id SELECT RecurseKey ,code_id ,id ,qty ,RunningTotal ,LSF ,1 GroupNum from cteBaseAgg where RecurseKey = 1 -- For each succesive row in each set, check if need to increment GroupNum UNION ALL SELECT agg.RecurseKey ,agg.code_id ,agg.id ,agg.qty ,agg.RunningTotal ,agg.LSF ,rec.GroupNum + case when rec.LSF = agg.LSF then 0 else 1 end from cteBaseAgg agg inner join cteRecurse rec on rec.code_id = agg.code_id and agg.RecurseKey - 1 = rec.RecurseKey ) -- Show results SELECT id ,code_id ,qty ,RunningTotal ,LSF ,GroupNum from cteRecurse order by code_id ,id
Sorry for making this question a bit more complicated than it needed to be but for the sake of closure I have found a solution using the lag function. In order to achieve what I wanted I continued my cte above with the following: , a as ( SELECT *, Lag(LSF, 1, LSF) OVER(Partition By code_id ORDER BY id) AS prev_LSF, Lag(code_id, 1, code_id) OVER(Partition By code_id ORDER BY id) AS prev_code FROM LongShortFlat ), b as ( SELECT id, LSF, code_id, Sum(CASE WHEN LSF <> prev_LSF AND code_id = prev_code THEN 1 ELSE 0 END) OVER(Partition By code_id ORDER BY id) AS grp FROM a ) select * from b order by id Here is the updated sqlfiddle.
SQL Recursive CTE unexpectedly returns alternating sets
I am trying to get the use recursive CTE to repeat the same pattern over and over, resetting when "Scenario" increases in value. RowNumber repeats 1-21 (as desired), but whenever "Scenario" is an even number, there are too few items in the "Vals" column to feed into "Value". I can't figure out which part of the code is causing me to be 1 short for only even Scenarios. Below are the results of the code I'm using at the bottom. Scenario RowNumber Value Vals 1 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 6 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 7 A A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 8 A A,A,A,A,A,A,A,A,A,A,A,B,C 1 9 A A,A,A,A,A,A,A,A,A,A,B,C 1 10 A A,A,A,A,A,A,A,A,A,B,C 1 11 A A,A,A,A,A,A,A,A,B,C 1 12 A A,A,A,A,A,A,A,B,C 1 13 A A,A,A,A,A,A,B,C 1 14 A A,A,A,A,A,B,C 1 15 A A,A,A,A,B,C 1 16 A A,A,A,B,C 1 17 A A,A,B,C 1 18 A A,B,C 1 19 A B,C 1 20 B C 1 21 C 2 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 5 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 6 A A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 7 A A,A,A,A,A,A,A,A,A,A,B,B,C 2 8 A A,A,A,A,A,A,A,A,A,B,B,C 2 9 A A,A,A,A,A,A,A,A,B,B,C 2 10 A A,A,A,A,A,A,A,B,B,C 2 11 A A,A,A,A,A,A,B,B,C 2 12 A A,A,A,A,A,B,B,C 2 13 A A,A,A,A,B,B,C 2 14 A A,A,A,B,B,C 2 15 A A,A,B,B,C 2 16 A A,B,B,C 2 17 A B,B,C 2 18 B B,C 2 19 B C 2 20 C 2 21 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 3 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 6 A A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 7 A A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 8 A A,A,A,A,A,A,A,A,A,A,B,C,C 3 9 A A,A,A,A,A,A,A,A,A,B,C,C 3 10 A A,A,A,A,A,A,A,A,B,C,C 3 11 A A,A,A,A,A,A,A,B,C,C 3 12 A A,A,A,A,A,A,B,C,C 3 13 A A,A,A,A,A,B,C,C 3 14 A A,A,A,A,B,C,C 3 15 A A,A,A,B,C,C 3 16 A A,A,B,C,C 3 17 A A,B,C,C 3 18 A B,C,C 3 19 B C,C 3 20 C C 3 21 C 4 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 4 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 5 A A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 6 A A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 7 A A,A,A,A,A,A,A,A,A,B,B,B,C 4 8 A A,A,A,A,A,A,A,A,B,B,B,C 4 9 A A,A,A,A,A,A,A,B,B,B,C 4 10 A A,A,A,A,A,A,B,B,B,C 4 11 A A,A,A,A,A,B,B,B,C 4 12 A A,A,A,A,B,B,B,C 4 13 A A,A,A,B,B,B,C 4 14 A A,A,B,B,B,C 4 15 A A,B,B,B,C 4 16 A B,B,B,C 4 17 B B,B,C 4 18 B B,C 4 19 B C 4 20 C This is the code I used to generate the above sample. Where am I going wrong? CREATE TABLE #temp3 ( Scenario INT ,Vals VARCHAR(64) ,LEN INT ) ; WITH vals AS ( SELECT v.* FROM (VALUES ('A'), ('B'), ('C')) v(x) ), CTE AS ( SELECT CAST('A' AS VARCHAR(MAX)) AS STR, 0 AS LEN UNION ALL SELECT (CTE.STR + ',' + vals.x), CTE.LEN + 1 FROM CTE JOIN vals ON vals.x >= RIGHT(CTE.STR, 1) WHERE CTE.LEN < 19 ) INSERT INTO #temp3 SELECT ROW_NUMBER() OVER(ORDER BY STR + ',C') AS Scenario ,STR + ',C' AS Vals ,LEN FROM CTE WHERE STR + 'C' LIKE '%B%' AND LEN = 19 ; -- Split strings created above into individual characters WITH cte(Scenario, Value, Vals) AS ( SELECT Scenario ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals FROM #temp3 UNION ALL SELECT Scenario ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') FROM cte WHERE Vals > '' ) SELECT Scenario ,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY Scenario) RowNumber ,Value ,Vals FROM cte t
I'm not exactly sure what the problem you are describing is, but the ROW_NUMBER() should use an ORDER BY clause that completely orders the rows in each partition. When you use "PARTITION BY Scenario ORDER BY Scenario" the order in which the ROW_NUMBER() values are assigned is undefined. Try something like WITH cte(Scenario, depth, Value, Vals) AS ( SELECT Scenario, 0 depth ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals FROM #temp3 UNION ALL SELECT Scenario, depth+1 ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') FROM cte WHERE Vals > '' ) SELECT Scenario ,depth ,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY depth ) RowNumber ,Value ,Vals FROM cte t