How to get below query - sql

I have a table with one field name as City.
Below are the data.
City
--------
A
B
C
D
E
F
G
H
Output will be as Column as well
Write a query or stored procedure or function?
If a pass 2, the query result will be below
Col1 Col2
A B
C D
E F
G H
If a pass 3, the query result will be below
Col1 Col2 Col3
A B C
D E F
G H
If a pass 4, the query result will be below
Col1 Col2 Col3 Clo4
A B C D
E F G H
--Here script for creating and adding data into temp table
create table #Cities( City varchar(max) )
insert into #Cities values
( 'A' ),
( 'B' ),
( 'C' ),
( 'D' ),
( 'E' ),
( 'F' ),
( 'G' ),
( 'H' )
declare #what_I_pass as int = 2; -- Here pass the number you want.
with Q1 as
(
select *, ROW_NUMBER() over (order by City) - 1 as n
from #Cities
),
Q2 as
(
select City, n / #what_I_pass as rn, n % #what_I_pass as cn
from Q1
)
select (stuff((select ' ' + City from Q2 as q where q.rn = Q2.rn order by cn for xml path('')), 1, 1, '')) as Result
from Q2
group by rn
order by rn

This would do:
declare #Cols int = 3 -- We define here how many output columns we want: 1, 2, 3, ... up to 10
create table #Cities( City varchar(max) )
insert into #Cities values
( 'A' ),
( 'B' ),
( 'C' ),
( 'D' ),
( 'E' ),
( 'F' ),
( 'G' ),
( 'H' )
;
with Cities as (
select City,
row_number() over (order by City) - 1 as Num
from #Cities
)
select Col1.City as Col1, Col2.City as Col2, Col3.City as Col3, Col4.City as Col4, Col5.City as Col5, Col6.City as Col6, Col7.City as Col7, Col8.City as Col8, Col9.City as Col9, Col10.City as Col10
from Cities as Rows
left join Cities as Col1 on 0 < #Cols and Col1.Num = Rows.Num * #Cols + 0
left join Cities as Col2 on 1 < #Cols and Col2.Num = Rows.Num * #Cols + 1
left join Cities as Col3 on 2 < #Cols and Col3.Num = Rows.Num * #Cols + 2
left join Cities as Col4 on 3 < #Cols and Col4.Num = Rows.Num * #Cols + 3
left join Cities as Col5 on 4 < #Cols and Col5.Num = Rows.Num * #Cols + 4
left join Cities as Col6 on 5 < #Cols and Col6.Num = Rows.Num * #Cols + 5
left join Cities as Col7 on 6 < #Cols and Col7.Num = Rows.Num * #Cols + 6
left join Cities as Col8 on 7 < #Cols and Col8.Num = Rows.Num * #Cols + 7
left join Cities as Col9 on 8 < #Cols and Col9.Num = Rows.Num * #Cols + 8
left join Cities as Col10 on 9 < #Cols and Col10.Num = Rows.Num * #Cols + 9
where Rows.Num <= (select max(Num) from Cities) / #Cols
drop table #Cities
Result for an input of 3 :
Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10
A B C NULL NULL NULL NULL NULL NULL NULL
D E F NULL NULL NULL NULL NULL NULL NULL
G H NULL NULL NULL NULL NULL NULL NULL NULL
PS: To avoid using dynamic SQL this query always returns a fixed number of 10 columns, but just the desired columns are filled. Now your presentation layer only needs to hide the unwanted empty columns.

Try below query:
--Here script for creating and adding data into temp table
declare #tbl table ( City varchar(max) )
insert into #tbl values
( 'A' ),
( 'B' ),
( 'C' ),
( 'D' ),
( 'E' ),
( 'F' ),
( 'G' ),
( 'H' )
declare #howManyCols int = 3;
;with cte as (
select City, grp,
row_number() over (partition by grp order by City) rn
from (
select *,
(row_number() over (order by City) - 1) % #howManyCols + 1 grp
from #tbl
) a
)
-- this query can be generated by dynamic SQL, because you can see pattern in following lft join's
select c1.City, c2.City, c3.City
from cte c1
left join cte c2 on c1.grp = c2.grp - 1 and c1.rn = c2.rn
left join cte c3 on c2.grp = c3.grp - 1 and c2.rn = c3.rn
where c1.grp = 1
For number of columns equal to three you get:

Try this script,
declare #flg int=3--test with 1,2,3,4 ...
create table #temp(id int identity(1,1),col varchar(10))
insert into #temp values ('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H')
create table #temp1(id int ,col varchar(10),flag int,rownum int)
declare #PvtCol varchar(500)=''
declare #Headcol varchar(500)
declare #Sql nvarchar(500)
WITH CTE
AS (SELECT id,
col,
CASE
WHEN(id % #flg) = 0
THEN #flg
ELSE(id % #flg)
END flag
FROM #temp),
CTE1
AS (SELECT *,
ROW_NUMBER() OVER(PARTITION BY flag
ORDER BY id) rownum
FROM cte)
INSERT INTO #temp1
(id,
col,
flag,
rownum
)
SELECT id,
col,
flag,
rownum
FROM cte1;
SELECT #PvtCol = COALESCE(#PvtCol + ', ' + QUOTENAME(rownum), QUOTENAME(rownum)),
#Headcol = COALESCE(#Headcol + ', ' + QUOTENAME(rownum) + ' as ' + 'col', QUOTENAME(rownum) + ' as ' + 'col') + CAST(rownum AS VARCHAR)
FROM #temp1
WHERE flag = 1;
SET #PvtCol = STUFF(#PvtCol, 1, 1, '');
SELECT #PvtCol,
#Headcol;
SET #Sql = 'select flag,' + #Headcol + ' from
(
select flag, col,rownum from #temp1
)src
pivot(max(col) for rownum in(' + #PvtCol + ')) as pvt';
PRINT #Sql;
EXECUTE sp_executesql
#Sql;
DROP TABLE #temp, #temp1;

Related

Pivoting row's to columns

How to achieve the below??
Anyone help me out
col_1 col_2
A 1
B 1
C 1
B 2
C 4
A 2
A 6
Output:
A B C
1 1 1
2 2 4
6
This will do the job, but it seems like quite an odd thing to want to do, so I am probably missing something?
CREATE TABLE #table (col1 CHAR(1), col2 INT);
INSERT INTO #table SELECT 'A', 1;
INSERT INTO #table SELECT 'B', 1;
INSERT INTO #table SELECT 'C', 1;
INSERT INTO #table SELECT 'B', 2;
INSERT INTO #table SELECT 'C', 4;
INSERT INTO #table SELECT 'A', 2;
INSERT INTO #table SELECT 'A', 6;
WITH Ranked AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY col1 ORDER BY col2) AS rank_id
FROM
#table),
Numbers AS (
SELECT 1 AS number
UNION ALL
SELECT number + 1 FROM Numbers WHERE number < 50)
SELECT
MAX(CASE WHEN col1 = 'A' THEN col2 END) AS [A],
MAX(CASE WHEN col1 = 'B' THEN col2 END) AS [B],
MAX(CASE WHEN col1 = 'C' THEN col2 END) AS [C]
FROM
Numbers n
INNER JOIN Ranked r ON r.rank_id = n.number
GROUP BY
n.number;
Results are:
A B C
1 1 1
2 2 4
6 NULL NULL
Looks like you are trying to pivot without aggregation? Here is another option:
select A, B, C from
( select col1, col2, dense_rank() over (partition by col1 order by col2) dr from #table) t
pivot
( max(t.col2) for t.col1 in (A, B, C)) pvt;
Also check this out for more examples/discussion: TSQL Pivot without aggregate function

Count pair-wise occurrences in a T-SQL table

How can I count pair-wise occurrences in a SQL Server table? Please note that the order of the given sequence has to be accounted for and shouldn't be changed.
Original table:
1 2 3 4
--------
1 | A A A B
2 | A # don't count
3 | B A A
4 | B # don't count
Result:
1 | AA = 3
2 | AB = 1
3 | BB = 0
4 | BA = 1
In addition, the code has to work for large datasets.
Edit:
A pair in this context is a set of two values {x[ij], x[(i+1)j]}, where i=1,...,4 and j=1,...,4. Further, pairs that have the form A null or B null shouldn't be counted. Moreover, null A or null B can't happen, therefore they don't have to be accounted for.
I just want to point out a pretty easy way to express this logic:
with vals as (
select 'A' as val union all select 'B'
)
pairs as (
select t1.val as val1, t2.val as val2
from vals t1 cross join vals t2
)
select p.*,
(select count(*)
from original
where [1] = val1 and [2] = val2 or
[2] = val1 and [3] = val2 or
[3] = val1 and [4] = val2
) as cnt
from pairs p
order by cnt desc;
This doesn't have great performance characteristics, that is actually easily fixed by using three subqueries and indexes on the data columns.
LiveDemo
CREATE TABLE #tab([1] NVARCHAR(100), [2] NVARCHAR(100),
[3] NVARCHAR(100), [4] NVARCHAR(100));
INSERT INTO #tab
VALUES ('A', 'A', 'A', 'B') ,('A' , NULL ,NULL ,NULL )
,('B' ,'A' ,'A', NULL),('B', NULL, NULL, NULL);
WITH cte AS
(
SELECT pair = [1] + [2] FROM #tab
UNION ALL
SELECT pair = [2] + [3] FROM #tab
UNION ALL
SELECT pair = [3] + [4] FROM #tab
), cte2 AS
(
SELECT [1] AS val FROM #tab
UNION ALL SELECT [2] FROM #tab
UNION ALL SELECT [3] FROM #tab
UNION ALL SELECT [4] FROM #tab
), all_pairs AS
(
SELECT DISTINCT a.val + b.val AS pair
FROM cte2 a
CROSS JOIN cte2 b
WHERE a.val IS NOT NULL and b.val IS NOT NULL
)
SELECT a.pair, result = COUNT(c.pair)
FROM all_pairs a
LEFT JOIN cte c
ON a.pair = c.pair
GROUP BY a.pair;
How it works:
cte create all pairs (1,2), (2,3), (3,4)
cte2 get all values from column
all_pairs create all possible pairs of values AA, AB, BA, BB
Final use grouping and COUNT to get number of occurences.
EDIT:
You can concatenate result as below:
LiveDemo2
...
, final AS
(
SELECT a.pair, result = COUNT(c.pair), rn = ROW_NUMBER() OVER(ORDER BY a.pair)
FROM all_pairs a
LEFT JOIN cte c
ON a.pair = c.pair
GROUP BY a.pair
)
SELECT rn, [result] = pair + ' = ' + CAST(result AS NVARCHAR(100))
FROM final
with cte as (
select 1 as id, 'A' as [1], 'A' as [2], 'A' as [3], 'B' as [4]
union all select 2 , 'A', NULL,NULL,NULL
union all select 3 , 'B', 'A','A',NULL
union all select 4 , 'B',NULL,NULL,NULL
)
, Vals as (
select 'AA' as Val
union all select 'AB'
union all select 'BB'
union all select 'BA'
)
, UNPVT as (
/*UNPIVOT to convert the columns to be rows*/
SELECT id , VAL + LEAD(VAL) OVER (PARTITION BY ID ORDER BY SEQ) as Code
FROM (
select ID,[1],[2],[3],[4] from cte
) P
UNPIVOT (Val FOR Seq IN ([1],[2],[3],[4])
) AS UNPVT
)
select Vals.Val, count(UNPVT.Code) from UNPVT right join Vals on UNPVT.Code = Vals.Val
group by Vals.Val
CTE: contains your data.
Vals: contains the returned code.
UnPVT: to convert the columns to be rows.

How can I concatenate values from 3 column per id?

Consider the following table #temp:
objectid field1 field2 field3
--------------------------------
1 X 001 foo
2 Y 022 bar
2 Z 033 baz
3 A 111 abc
3 B 222 def
3 C 333 ghi
4 Q 900 tom
I need the concatenate field1, field2 and field3 for each objectid:
objectid field1 field2 field3
------------------------------------------
1 X 001 foo
2 Y;Z 022;033 barbaz
3 A;B;C 111;222;333 abc;def;ghi
4 Q 900 tom
I trief this first for only field1 as follows:
select
c.FLUSObjectNumber,
[field1] = stuff(
(
select ';' + field1
from #temp as c2
where c2.objectid = c.objectid and c2.field1 = c.field1
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, ''
)
from #temp c
group by c.objectid, c.field1
;
However, this returns the list I already have in #temp.
What am I doing wrong? How can I generate the desired output?
declare #t table (objectid INT, field1 VARCHAR(50), field2 VARCHAR(50), field3 VARCHAR(50))
INSERT INTO #t (objectid,field1,field2,field3)values (1,'X','001','foo')
INSERT INTO #t (objectid,field1,field2,field3)values (2,'Y','022','bar')
INSERT INTO #t (objectid,field1,field2,field3)values (2,'Z','033','baz')
INSERT INTO #t (objectid,field1,field2,field3)values (3,'A','111','abc')
INSERT INTO #t (objectid,field1,field2,field3)values (3,'B','222','def')
INSERT INTO #t (objectid,field1,field2,field3)values (3,'C','333','ghi')
INSERT INTO #t (objectid,field1,field2,field3)values (4,'Q','900','tom')
Select distinct t.objectid ,
STUFF((Select distinct ',' + t1.field1
from #t t1
where t.objectid = t1.objectid
ORDER BY 1
FOR XML PATH(''))
, 1, 1, '')As field,
STUFF((Select distinct ',' + t2.field2
from #t t2
where t.objectid = T2.objectid
ORDER BY 1
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)')
, 1, 1, ' ')As field2,
STUFF((Select distinct ',' + T3.field3
from #t t3
where t.objectid = t3.objectid
ORDER BY 1
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)')
, 1, 1, ' ')As field3
from #t t
group by
t.objectid
DECLARE #Temp TABLE
(
objectid INT,
field1 VARCHAR(20),
field2 VARCHAR(20),
field3 VARCHAR(20)
)
INSERT INTO #Temp
( objectid, field1, field2, field3 )
VALUES
(1 ,'X' ,'001' ,'foo'),
(2 ,'Y' ,'022' ,'bar'),
(2 ,'Z' ,'033' ,'baz'),
(3 ,'A' ,'111' ,'abc'),
(3 ,'B' ,'222' ,'def'),
(3 ,'C' ,'333' ,'ghi'),
(4 ,'Q' ,'900' ,'tom');
SELECT DISTINCT t.objectid, f1.f1, f2.f2, f3.f3
FROM #Temp t
OUTER APPLY
(
SELECT STUFF((SELECT ',' + Field1 FROM #Temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'') f1
) f1
OUTER APPLY
(
SELECT STUFF((SELECT ',' + Field2 FROM #Temp WHERE objectid = t.objectid ORDER BY field2 FOR XML PATH('')),1,1,'') f2
) f2
OUTER APPLY
(
SELECT STUFF((SELECT ',' + Field3 FROM #Temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'') f3
) f3
OUTPUT:
objectid f1 f2 f3
1 X 001 foo
2 Y,Z 022,033 bar,baz
3 A,B,C 111,222,333 abc,def,ghi
4 Q 900 tom
You don't need outer apply or anything, try this:
SELECT DISTINCT t.objectid,
(SELECT STUFF((SELECT ';' + Field1 FROM #temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'')) AS [field1],
(SELECT STUFF((SELECT ';' + Field2 FROM #temp WHERE objectid = t.objectid ORDER BY field2 FOR XML PATH('')),1,1,'')) AS [field2],
(SELECT STUFF((SELECT ';' + Field3 FROM #temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'')) AS [field3]
FROM #temp t
you missed taking field1 within the STUFF clause from the correct table.
select
c.FLUSObjectNumber,
[field1] = stuff(
(
select ';' + c2.field1
from #temp as c2
where c2.objectid = c.objectid and c2.field1 = c.field1
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, ''
)
from #temp c
group by c.objectid, c.field1
;

SQL count ids in fields

I have a table contains IDs in field. It looks like:
FieldName
-------------------------
1,8,2,3,4,10,5,9,6,7
-------------------------
1,8
-------------------------
1,8
I need to count these IDs to get result:
ID | Count
---|------
1 | 3
8 | 3
2 | 1
3 | 1
Any ideas?
Thanks!
Try this :
Declare #demo table(FieldName varchar(100))
insert into #demo values('1,8,2,3,4,10,5,9,6,7')
insert into #demo values('1,8')
insert into #demo values('1,8')
select ID, COUNT(id) ID_count from
(SELECT
CAST(Split.a.value('.', 'VARCHAR(100)') AS INT) AS ID
FROM
(
SELECT CAST ('<M>' + REPLACE(FieldName, ',', '</M><M>') + '</M>' AS XML) AS ID
FROM #demo
) AS A CROSS APPLY ID.nodes ('/M') AS Split(a)) q1
group by ID
I like Devart's answer because of the faster execution. Here is a modified earlier answer to suite your need :
Declare #col varchar(200)
SELECT
#col=(
SELECT FieldName + ','
FROM #demo c
FOR XML PATH('')
);
;with demo as(
select cast(substring(#col,1,charindex(',',#col,1)-1) AS INT) cou,charindex(',',#col,1) pos
union all
select cast(substring(#col,pos+1,charindex(',',#col,pos+1)-pos-1)AS INT) cou,charindex(',',#col,pos+1) pos
from demo where pos<LEN(#col))
select cou ID, COUNT(cou) id_count from demo
group by cou
Try this one -
Query:
SET NOCOUNT ON;
DECLARE #temp TABLE (txt VARCHAR(8000))
INSERT INTO #temp (txt)
VALUES ('1,8,2,3,4,10,5,9,6,7'), ('1,8'), ('1,8')
SELECT
t.ID
, [Count] = COUNT(1)
FROM (
SELECT
ID =
SUBSTRING(
t.string
, number + 1
, ABS(CHARINDEX(',', t.string, number + 1) - number - 1)
)
FROM (
SELECT string = (
SELECT ',' + txt
FROM #temp
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)')
) t
CROSS JOIN [master].dbo.spt_values n
WHERE [type] = 'p'
AND number <= LEN(t.string) - 1
AND SUBSTRING(t.string, number, 1) = ','
) t
GROUP BY t.ID
ORDER BY [Count] DESC
Output:
ID Count
----- -----------
1 3
8 3
9 1
10 1
2 1
3 1
4 1
5 1
6 1
7 1
Query cost:

Problem in counting nulls and then merging them with the existing rows

Input:
ID groupId RowID Data
1 1 1 W
2 1 1 NULL
3 1 1 NULL
4 1 1 Z
5 1 2 NULL
6 1 2 NULL
7 1 2 X
8 1 2 NULL
9 1 3 NULL
10 1 3 NULL
11 1 3 Y
12 1 3 NULL
Expected Output
GroupId NewData
1 2Y1,2X1,W2Z
For every Null there will be a numeric count. That is if there are two nulls then the numeric value will be 2.
The ddl is as under
DECLARE #t TABLE(ID INT IDENTITY(1,1) , GroupId INT, RowID INT, Data VARCHAR(10))
INSERT INTO #t (GroupId, RowID,DATA)
SELECT 1,1,'W' UNION ALL SELECT 1,1,NULL UNION ALL SELECT 1,1,NULL UNION ALL SELECT 1,1,'Z' UNION ALL SELECT 1,2,NULL UNION ALL
SELECT 1,2,NULL UNION ALL SELECT 1,2,'X' UNION ALL SELECT 1,2,NULL UNION ALL SELECT 1,3,NULL UNION ALL SELECT 1,3,NULL UNION ALL
SELECT 1,3,'Y' UNION ALL SELECT 1,3,NULL
select * from #t
My version is as under but not the correct output
;with t as (
select GroupID, id, RowID, convert(varchar(25), case when Data is null then '' else Data end) Val,
case when Data is null then 1 else 0 end NullCount from #t where id = 1
union all
select t.GroupID, a.id,a.RowID, convert(varchar(25), Val +
case when Data is not null or (t.RowID <> a.RowID and NullCount > 0) then ltrim(NullCount) else '' end +
case when t.RowID <> a.RowID then ',' else '' end + isnull(Data, '')),
case when Data is null then NullCount + 1 else 0 end NullCount
from t inner join #t a on t.GroupID = a.GroupID and t.id + 1 = a.id
)
select GroupID, Data = Val + case when NullCount > 0 then ltrim(NullCount) else '' end from t
where id = (select max(id) from #t where GroupID = t.GroupId)
Is yielding the below output
GroupID Data
1 W2Z,2X1,3Y1
Please help me out
Thanks in advance
Kind of messy and most likely can be improved
;With RawData AS
(
select * from #t
)
,Ranked1 as
(
select *, RANK() OVER (PARTITION BY GroupId, RowID ORDER BY ID, GroupId, RowID) R from #t
)
,Ranked2 as
(
select *, R - RANK() OVER (PARTITION BY GroupId, RowID ORDER BY ID, GroupId, RowID) R2 from Ranked1
where Data is null
)
,Ranked3 as
(
select MIN(ID) as MinID, GroupId, RowID, R2, COUNT(*) C2 from Ranked2
group by GroupId, RowID, R2
)
,Ranked4 as
(
select RD.ID, RD.GroupId, RD.RowID, ISNULL(Data, C2) as C3 from RawData RD
left join Ranked3 R3 on RD.ID = R3.MinID and RD.GroupId = R3.GroupId and RD.RowID = R3.RowID
where ISNULL(Data, C2) is not null
)
,Grouped as
(
select GroupId, RowID,
(
select isnull(C3, '') from Ranked4 as R41
where R41.GroupId = R42.GroupId and R41.RowID = R42.RowID
order by GroupId, RowID for xml path('')
) as C4
from Ranked4 as R42
group by GroupId, RowID
)
select GroupId,
stuff((
select ',' + C4 from Grouped as G1
where G1.GroupId = G2.GroupId
order by GroupId for xml path('')
), 1, 1, '')
from Grouped G2
group by GroupId