SQL Server Trying to convert the data from rows to columns - sql

I have table with data like as below
Crosswalk_id Code
ORG_201 1234
ORG_201 3456
ORG_201 3459
ORG_201 0983
ORG_201 2562
ORG_201 1239
need to convert this into
Crosswalk_id C1 C2 C3 C4 C5 C6
ORG_201 1234 3456 3459 0983 2562 1239
Please help me to achieve this using pivot

Use conditional aggregation with row_number():
select crosswalk_id,
max(case when seqnum = 1 then code end) as c1,
max(case when seqnum = 2 then code end) as c2,
max(case when seqnum = 3 then code end) as c3,
max(case when seqnum = 4 then code end) as c4,
max(case when seqnum = 5 then code end) as c5,
max(case when seqnum = 6 then code end) as c6
from (select t.*,
row_number() over (partition by crosswalk_id order by (select null)) as seqnum
from t
) t
group by crosswalk_id;

You can use the SQL Server PIVOT function, it's made exactly for this purpose, i.e. transform rows to columns.
An example of the PIVOT Function is available here on this blog.
You must include an aggregation clause, the MAX() works pretty well for string values if you do not have duplicates lines.
For the full documentation, please check the official Microsoft doc covering PIVOT and UNPIVOT functions.
NOTE: in your example, you only have one value "ORG_201", you might have more to use the function. And you need to name the columns, based on the values you expect in the [Code] column.

Don't your IDs need to be different???
-- DROP TABLE mytable
CREATE table mytable (
id NVARCHAR(20),
code NVARCHAR(5)
)
GO
INSERT INTO mytable ( id, code ) VALUES ( 'ORG_201', '1234')
INSERT INTO mytable ( id, code ) VALUES ( 'ORG_202', '3456')
INSERT INTO mytable ( id, code ) VALUES ( 'ORG_203', '3459')
INSERT INTO mytable ( id, code ) VALUES ( 'ORG_204', '0983')
INSERT INTO mytable ( id, code ) VALUES ( 'ORG_205', '2562')
INSERT INTO mytable ( id, code ) VALUES ( 'ORG_206', '1239')
SELECT *
FROM mytable
select * from
(
SELECT id, code
FROM mytable
) t
PIVOT (
max(code)
FOR id IN ([ORG_201],[ORG_202],[ORG_203],[ORG_204],[ORG_205],[ORG_206])
) AS PivotTable;

Related

how to apply max clause on column other than group by columns in Hive

I have a hive table that contains data like below.
Table
---------------------
c1 c2 c3
a 1 7
a 2 6
a 3 3
a 3 1
a 3 2
I want to write a query to get value 2 from c3 column. The logic is, for column c1 select max(c2) and then within that max(c2) find max(c3)
I wrote query like
select c1, max(c3) from table1
group by c1
having c2=max(c2)
but this did not work as Hive says that I can use only those columns in having clause that are part of group by.
Please help me with this.
Note:- I need a single query for this. I am able to write the same in two queries
with your_data as (
select stack (5,
'a',1,7,
'a',2,6,
'a',3,3,
'a',3,1,
'a',3,2) as (c1,c2,c3)
)
select c1, max(c3) as max_c3
from
(
select c1,c2,c3,
rank() over(partition by c1 order by c2 desc) rn --max(c2) marked 1
from your_data
)s where rn=1 --filter records with max(c2)
group by c1
Result:
c1 max_c3
a 3
Using aggregate function:
create table val
(alpha varchar(10),id1 int,id2 int);
insert into val values ('a',3,3);
insert into val values ('a',3,1);
insert into val values ('a',3,2);
select alpha,id2 from
(
select alpha,max(id1) as id1,max(id2) as id2
from val group by alpha
)agg

SQL Row_Number() increase only when the value = 1 for a specific column

I am having some trouble generating row_number() in my SQL query as my expectation. I have this following output of my query-
Now, I want to add row number for all rows where row number will only increase when the value in C1 is = 1. Required output as below-
Any help will be appreciated. TIA
Table Variable:
DECLARE #Table AS TABLE (C1 INT)
INSERT INTO #Table VALUES (1),(4),(1),(1),(4),(1),(3),(4)
SQL 2008 Version
;WITH cteSimulateAnOriginalIdentityKey AS (
SELECT
C1
,OriginalOrder = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
#Table
)
, cteC1RowNumber AS (
SELECT
*
,C1RowNumber = ROW_NUMBER() OVER (PARTITION BY C1 ORDER BY OriginalOrder)
FROM
cteSimulateAnOriginalIdentityKey
)
SELECT
C1
,RN = ISNULL((SELECT MAX(C1RowNumber) FROM cteC1RowNumber r2 WHERE r2.C1 = 1 AND r2.OriginalOrder <= r1.OriginalOrder),1)
FROM
cteC1RowNumber r1
ORDER BY
OriginalOrder
SQL 2012+ version
;WITH cteSimulateAnOriginalIdentityKey AS (
SELECT
C1
,OriginalOrder = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
#Table
)
, cteC1RowNumber AS (
SELECT
*
,C1RowNumber = ROW_NUMBER() OVER (PARTITION BY C1 ORDER BY OriginalOrder)
FROM
cteSimulateAnOriginalIdentityKey
)
SELECT
C1
,RN = ISNULL(MAX(CASE WHEN C1 = 1 THEN C1RowNumber END) OVER (ORDER BY OriginalOrder),1)
FROM
cteC1RowNumber
ORDER BY
OriginalOrder
RESULT:
C1 RN
1 1
4 1
1 2
1 3
4 3
1 4
3 4
4 4
If you in fact have another column by which to maintain the desired original order you don't need the first cte which is simply simulating that column
Try this:
SELECT C1,
ROW_NUMBER() OVER (PARTITION BY C1 ORDER BY (SELECT 100)) RN
FROM TableNAme
I presume you have another column(s) in your query by which you determine the order of rows; without such a criteria, your whole question is pointless.
The query below will work on SQL Server 2012 or later versions:
declare #Table table (
Id int identity(1,1) not null,
C1 int
);
insert into #Table(C1) values (1),(4),(1),(1),(4),(1),(3),(4);
select t.C1,
sum(case t.C1 when 1 then 1 else 0 end) over(order by t.Id) as [RN]
from #Table t;

T_SQL query extreme value

There is a table T, with a random value in id, How with one select we can get extreme value of id in input .
example :
T.id =
12
34
76
89
1234
1254
6789
3456
For input we give select id=1254, as output we have to get two values 1234 and 6789
You can do It in following:
SAMPLE DATE
CREATE TABLE #Test (ID INT)
INSERT INTO #Test VALUES (12),(34),(76),(89),(1234),(1254),(6789),(3456)
INPUT
DECLARE #var INT = 1234
QUERY
;WITH cte AS
(
SELECT Id,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) rn1
FROM #Test t
)
SELECT PrevId, NextId
FROM cte
LEFT JOIN (
SELECT Id PrevId,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) rn
FROM #Test t1
) previd ON cte.rn1 = previd.rn +1
LEFT JOIN (
SELECT Id NextId,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) rn
FROM #Test t1
) nextid ON cte.rn1 = nextid.rn -1
WHERE cte.Id = #var
OUTPUT
PrevId NextId
89 1254
DEMO
You can test It at SQL FIDDLE
You can use conditional aggregation:
select max(case when id < 1254 then id end) as prev,
min(case when id > 1254 then id end) as next
from t;
A similar approach produces two rows but is more efficient if you have indexes:
select 'prev', max(id)
from t
where id < 1254
union all
select 'next', min(id)
from t
where id > 1254;
EDIT:
I seem to have missed that the ids are out of order. In that case, you need to assume that there is a column that specifies the ordering of the data. SQL tables represent unordered sets, so there is no next or previous value. You can handle this using window functions if you have a column for ordering:
with n as (
select t.*, row_number() over (order by <ordering column goes here>) as seqnum
from t
)
select max(case when seqnum = theseqnum - 1 then id end) as prev_id,
max(case when seqnum = theseqnum + 1 then id end) as next_id
from (select n.*,
max(case when id = 1254 then seqnum end) as theseqnum
from n
) n
where seqnum = theseqnum - 1 or seqnum = thesequm + 1;
You did not mention which SQL Server version you're using. If it's 2012+ you can use LAG() and LEAD() functions to achieve that quite fast:
LAG:
Accesses data from a previous row in the same result set without the
use of a self-join in SQL Server 2012+. LAG provides access to a row
at a given physical offset that comes before the current row. Use this
analytic function in a SELECT statement to compare values in the
current row with values in a previous row.
LEAD:
Accesses data from a subsequent row in the same result set without the
use of a self-join in SQL Server 2012+. LEAD provides access to a row
at a given physical offset that follows the current row. Use this
analytic function in a SELECT statement to compare values in the
current row with values in a following row.
Here's an example:
DECLARE #Test TABLE
(
ID INT
);
INSERT INTO #Test
VALUES (12)
, (34)
, (76)
, (89)
, (1234)
, (1254)
, (6789)
, (3456);
;WITH CTE (ID, Prev, Next, Extreme)
AS (
SELECT ID
, LAG(ID) OVER (ORDER BY (SELECT NULL))
, LEAD(ID) OVER (ORDER BY (SELECT NULL))
, MAX(ID) OVER ()
FROM #Test
)
SELECT *
FROM CTE
WHERE ID = 1234;
This query returns
╔══════╦══════╦══════╦═════════╗
║ ID ║ Prev ║ Next ║ Extreme ║
╠══════╬══════╬══════╬═════════╣
║ 1234 ║ 89 ║ 1254 ║ 6789 ║
╚══════╩══════╩══════╩═════════╝
As stated by lad2025, this and as well answered query will break if sql server optimizer decides to use parallelism.
Using OPTION(MAXDOP 1) hint might solve it. It tells sql server not to use parallelism. Only correct way would be having column, that you could give stable order for your data set.

SQL group by if values are close

Class| Value
-------------
A | 1
A | 2
A | 3
A | 10
B | 1
I am not sure whether it is practical to achieve this using SQL.
If the difference of values are less than 5 (or x), then group the rows (of course with the same Class)
Expected result
Class| ValueMin | ValueMax
---------------------------
A | 1 | 3
A | 10 | 10
B | 1 | 1
For fixed intervals, we can easily use "GROUP BY". But now the grouping is based on nearby row's value. So if the values are consecutive or very close, they will be "chained together".
Thank you very much
Assuming MSSQL
You are trying to group things by gaps between values. The easiest way to do this is to use the lag() function to find the gaps:
select class, min(value) as minvalue, max(value) as maxvalue
from (select class, value,
sum(IsNewGroup) over (partition by class order by value) as GroupId
from (select class, value,
(case when lag(value) over (partition by class order by value) > value - 5
then 0 else 1
end) as IsNewGroup
from t
) t
) t
group by class, groupid;
Note that this assumes SQL Server 2012 for the use of lag() and cumulative sum.
Update:
*This answer is incorrect*
Assuming the table you gave is called sd_test, the following query will give you the output you are expecting
In short, we need a way to find what was the value on the previous row. This is determined using a join on row ids. Then create a group to see if the difference is less than 5. and then it is just regular 'Group By'.
If your version of SQL Server supports windowing functions with partitioning the code would be much more readable.
SELECT
A.CLASS
,MIN(A.VALUE) AS MIN_VALUE
,MAX(A.VALUE) AS MAX_VALUE
FROM
(SELECT
ROW_NUMBER()OVER(PARTITION BY CLASS ORDER BY VALUE) AS ROW_ID
,CLASS
,VALUE
FROM SD_TEST) AS A
LEFT JOIN
(SELECT
ROW_NUMBER()OVER(PARTITION BY CLASS ORDER BY VALUE) AS ROW_ID
,CLASS
,VALUE
FROM SD_TEST) AS B
ON A.CLASS = B.CLASS AND A.ROW_ID=B.ROW_ID+1
GROUP BY A.CLASS,CASE WHEN ABS(COALESCE(B.VALUE,0)-A.VALUE)<5 THEN 1 ELSE 0 END
ORDER BY A.CLASS,cASE WHEN ABS(COALESCE(B.VALUE,0)-A.VALUE)<5 THEN 1 ELSE 0 END DESC
ps: I think the above is ANSI compliant. So should run in most SQL variants. Someone can correct me if it is not.
These give the correct result, using the fact that you must have the same number of group starts as ends and that they will both be in ascending order.
if object_id('tempdb..#temp') is not null drop table #temp
create table #temp (class char(1),Value int);
insert into #temp values ('A',1);
insert into #temp values ('A',2);
insert into #temp values ('A',3);
insert into #temp values ('A',10);
insert into #temp values ('A',13);
insert into #temp values ('A',14);
insert into #temp values ('b',7);
insert into #temp values ('b',8);
insert into #temp values ('b',9);
insert into #temp values ('b',12);
insert into #temp values ('b',22);
insert into #temp values ('b',26);
insert into #temp values ('b',67);
Method 1 Using CTE and row offsets
with cte as
(select distinct class,value,ROW_NUMBER() over ( partition by class order by value ) as R from #temp),
cte2 as
(
select
c1.class
,c1.value
,c2.R as PreviousRec
,c3.r as NextRec
from
cte c1
left join cte c2 on (c1.class = c2.class and c1.R= c2.R+1 and c1.Value < c2.value + 5)
left join cte c3 on (c1.class = c3.class and c1.R= c3.R-1 and c1.Value > c3.value - 5)
)
select
Starts.Class
,Starts.Value as StartValue
,Ends.Value as EndValue
from
(
select
class
,value
,row_number() over ( partition by class order by value ) as GroupNumber
from cte2
where PreviousRec is null) as Starts join
(
select
class
,value
,row_number() over ( partition by class order by value ) as GroupNumber
from cte2
where NextRec is null) as Ends on starts.class=ends.class and starts.GroupNumber = ends.GroupNumber
** Method 2 Inline views using not exists **
select
Starts.Class
,Starts.Value as StartValue
,Ends.Value as EndValue
from
(
select class,Value ,row_number() over ( partition by class order by value ) as GroupNumber
from
(select distinct class,value from #temp) as T
where not exists (select 1 from #temp where class=t.class and Value < t.Value and Value > t.Value -5 )
) Starts join
(
select class,Value ,row_number() over ( partition by class order by value ) as GroupNumber
from
(select distinct class,value from #temp) as T
where not exists (select 1 from #temp where class=t.class and Value > t.Value and Value < t.Value +5 )
) ends on starts.class=ends.class and starts.GroupNumber = ends.GroupNumber
In both methods I use a select distinct to begin because if you have a dulpicate entry at a group start or end things go awry without it.
Here is one way of getting the information you are after:
SELECT Under5.Class,
(
SELECT MIN(m2.Value)
FROM MyTable AS m2
WHERE m2.Value < 5
AND m2.Class = Under5.Class
) AS ValueMin,
(
SELECT MAX(m3.Value)
FROM MyTable AS m3
WHERE m3.Value < 5
AND m3.Class = Under5.Class
) AS ValueMax
FROM
(
SELECT DISTINCT m1.Class
FROM MyTable AS m1
WHERE m1.Value < 5
) AS Under5
UNION
SELECT Over4.Class,
(
SELECT MIN(m4.Value)
FROM MyTable AS m4
WHERE m4.Value >= 5
AND m4.Class = Over4.Class
) AS ValueMin,
(
SELECT Max(m5.Value)
FROM MyTable AS m5
WHERE m5.Value >= 5
AND m5.Class = Over4.Class
) AS ValueMax
FROM
(
SELECT DISTINCT m6.Class
FROM MyTable AS m6
WHERE m6.Value >= 5
) AS Over4

row convert to column in sql 2008

I want to convert a series of rows into a series of columns
create table #cusphone(cusid int,cusph1 int)
insert into #cusphone values(1,48509)
insert into #cusphone values(1,48508)
insert into #cusphone values(1,48507)
insert into #cusphone values(2,48100)
so that the output is like this
1 48509 48508 48507
2 48100 null null
You can use the same approach of rank() and then use the new PIVOT function as follows:
with cusCte as(
select cusid,cusph1,RANK() over (partition by cusid order by cusph1) r
from #cusphone)
SELECT cusid, [1] AS C1, [2] AS C2, [3] AS C3
FROM
(SELECT cusid,cusph1,r
FROM cusCte) p
PIVOT
(
MIN (cusph1)
FOR r IN
( [1], [2], [3] )
) AS pvt;
You did not specify the rules by which something should appear in the first column vs the second column so I guessed that this is based on the occurrence (and thus sorting) of the cusph1 value.
With RankedItems As
(
Select cusid, cusph1
, ROW_NUMBER() OVER( PARTITION BY cusid ORDER BY cusph1 DESC) As Num
From #cusphone
)
Select cusid
, Min(Case When Num = 1 Then cusph1 End) As Col1
, Min(Case When Num = 2 Then cusph1 End) As Col2
, Min(Case When Num = 3 Then cusph1 End) As Col3
From RankedItems
Group By cusid