SQL Server Row to Cols [duplicate] - sql

This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
SQL Server: Examples of PIVOTing String data
(7 answers)
Closed 7 years ago.
I seems an simple task but I need some help from the sql experts with this one.
The number of contacts can vary from 1 to 30 persons and I want all the phone numbers in one row per cust_ref.
How can I do this??

This is a solution without dynamic sql, please try out:
declare #contacts table(cust_ref int,phone varchar(100));
insert into #contacts values(10000,'ph10000a'),(10000,'ph10000b'),(10000,'ph10000c')
,(10001,'ph10001a'),(10001,'ph10001b'),(10001,'ph10001c')
,(10002,'ph10002a'),(10002,'ph10002b');
SELECT p.*
FROM
(
SELECT 'phone'+REPLACE(STR(ROW_NUMBER() OVER(PARTITION BY cust_ref ORDER BY phone),2),' ','0') AS inx,*
FROM #contacts
) AS tbl
PIVOT
(
MIN(phone) FOR inx IN(phone01,phone02,phone03,phone04) --type more...
) AS p

by using CROSS APPLY,CTE and UNION ALL we can get the above requirement
declare #tableA table (RefId int,Phone varchar(10))
insert into #tableA (RefId,Phone)values (10000,'030123456'),(100123,060123445)
Declare #tableB table (CustID INT,RefId INT,Phone varchar(10)))
insert into #tableB (CustID,RefId,Phone)values
(1,10000,'030245789'),
(2,10000,'030245889'),
(1,100123,'060245789'),
(2,100123,'060243389'),
(3,100123,'060289389')
; with CTE AS (
select A.RefId,A.Phone As Phone from #tableA A
UNION
select B.RefId,B.Phone As Phone from #tableB B )
select * from (
Select RefId,
val,
COL + CAST(ROW_NUMBER()OVER(PARTITION BY RefId ORDER BY RefId) AS VARCHAR(1))RN
FROM CTE
CROSS APPLY (VALUES ('Phone',Phone))CS(Col,val))T
PIVOT (MAX(VAL) FOR RN IN ([Phone1],[Phone2],[Phone3],[Phone4]))P

Related

String split ,sort and compare with another column within a table SQL

Create Table #temp
(
Change varchar(20),
deleted_user_id int,
deleted_field_id int,
deleted_value nvarchar (4000),
inserted_user_id int,
inserted_field_id int,
inserted_value nvarchar (4000),
is_difference int,
)
insert into #temp values ('UPDATE','1', '11', '3,2,1,4','1','11','1,2,3,4','0')
insert into #temp values ('UPDATE','1', '12', '','1','12','1,2,3','0')
insert into #temp values ('UPDATE','2', '12', '1,2','2','12','','0')
select * from #temp
I am using Microsoft SQL server management studio
I am trying to compare the deleted value with inserted value. if there is a difference then set is_difference column to 1 or just return the rows.
so far I am thinking to do string split. maybe use stuff function. but not quite sure how to use all these to in a single query or is there any other option. Any help is appreciated. Thanks in advance
this query works so far.
;with cte as
(select isnull(deleted_user_id, inserted_user_id) as user_id,
isnull(deleted_field_id, inserted_field_id) as field_id,
stuff(
(
SELECT ','+ value FROM STRING_SPLIT (deleted_value, ',') group by value order by value FOR XML PATH('')
),1,1,'') as sorted_deleted_value , inserted_value
from #temp )
select * from cte where sorted_deleted_value <> inserted_value

How to create a query to join multiple rows from a different table into single comma delimated column in SQL Server [duplicate]

This question already has answers here:
Simulating group_concat MySQL function in Microsoft SQL Server 2005?
(12 answers)
Closed 4 years ago.
I am using SQL Server 2014, and I have two tables:
number id
------------------
36-23 1
36-23 2
id value
------------------
1 asia
2 europe
The number column is of type varchar. I want to write a query to return the following results:
number Name
---------------------------
36-23 asia,europe
I am wondering how can I do this with the help of query or functions in SQL Server.
I think using STUFF is the easiest way to go forward here.
CREATE TABLE tableID
([number] varchar(50), [id] int)
;
INSERT INTO tableID
([number], [id])
VALUES
('36-23', 1),
('36-23', 2)
;
CREATE TABLE tableLoc
([id] int, [value] varchar(50))
;
INSERT INTO tableLoc
([id], [value])
VALUES
(1, 'asia'),
(2, 'europe')
;
SELECT tableID.number, tableLoc.value INTO temp1
FROM tableID INNER JOIN tableLoc ON tableID.id = tableLoc.id;
SELECT *, STUFF((
SELECT DISTINCT ', ' + value
FROM temp1
WHERE number = t.number
FOR XML PATH('')), 1, 2, '')
FROM (
SELECT DISTINCT number
FROM temp1
) t

Join by concatenated field (in CSV format)

UPDATE: I am fully aware that this is a poor RDBMS practice, but the question is not asking whether it is and how I can re-train the DBAs who created this architecture. The question is how I can work around the situation that I have on hands. I appreciate the help of the community and must admit that this is an interesting problem indeed.
In SQL Server 2017, I have a lookup table containing codes and a transactions table with CSV-formated codes:
CREATE TABLE #t(cd VARCHAR(100))
CREATE TABLE #cd (id INT, cd VARCHAR (1000))
INSERT INTO #t SELECT 'c1'
INSERT INTO #t SELECT 'c1,c2'
INSERT INTO #t SELECT 'c1,c2,c3'
INSERT INTO #cd SELECT 10, 'c1'
INSERT INTO #cd SELECT 20, 'c2'
INSERT INTO #cd SELECT 30, 'c3'
So, the lookup is
id cd
10 c1
10 c1
20 c2
30 c3
and, the transactions table has:
cd
c1
c1,c2
c1,c2,c3
I need to replace the codes to their respective IDs, while keeping these in CSV format.
I would like to avoid the cursor because it is too slow. Is there a way to parse the codes, do the JOIN, and recombine the IDs somehow efficiently? I suppose COALESCE may be of use, but need help applying it. Perhaps, there is already a function in t-SQL that does this types of lookups.
The output needs to another column in transactions table:
id
10
10,20
10,20,30
You can first strip out comma into a list and then join and get correct ids for codes and then add them back with commas. I used a row_number upfront to get a unique thing to join back on in my query.
See live demo
CREATE TABLE #t(cd VARCHAR(100))
CREATE TABLE #cd (id INT, cd VARCHAR (1000))
INSERT INTO #t SELECT 'c1'
INSERT INTO #t SELECT 'c1,c2'
INSERT INTO #t SELECT 'c1,c2,c3'
INSERT INTO #cd SELECT 10, 'c1'
INSERT INTO #cd SELECT 20, 'c2'
INSERT INTO #cd SELECT 30, 'c3'
; WITH X AS
(
SELECT
C.id,P1.rn
FROM
(
SELECT *, row_number() over( order by (select 1)) rn,
cast('<X>'+replace(P.cd,',','</X><X>')+'</X>' as XML) AS xmlitems FROM #t P
)P1
CROSS APPLY
(
SELECT fdata.D.value('.','varchar(100)') AS splitdata
FROM P1.xmlitems.nodes('X') AS fdata(D)) O
LEFT JOIN #cd C
ON C.cd= LTRIM(RTRIM(O.splitdata ))
)
SELECT
rn,
id= STUFF((
SELECT ',' + cast(id as varchar(100)) FROM X AS x2
WHERE x2.rn = x.rn
ORDER BY rn FOR XML PATH,
TYPE).value(N'.[1]',N'varchar(max)'), 1, 1, '')
FROM
X
GROUP BY rn
Note: With SQL server 2017 you can also you SPLIT_STRING() function
and STRING_AGG() functions
SQL SERVER 2017 code:
select
id=STRING_AGG(id,',')
from
(
select V=value, rn
from
(
select
rn=row_number() over( order by (select 1)),
cd
from #T
)T
cross apply STRING_SPLIT(cd, ',')
) T
left join #cd C
on cd= v
group by rn

How to use pivot for columns of type varchar

My table is:
SBType|SBName|Qty
===================
SMDB SB01 1
SMDB SB01 4
SMDB SB02 2
SMDB SB02 5
SMDB SB03 3
SMDB SB03 6
My desired output is:
SB01 | SB02 | SB03
==================
1 2 3
4 5 6
This is what my code looks like:
SELECT *
FROM (
SELECT
SM.SBName,ISNULL(ES.Qty,0)Qty
FROM RE_ES_SwitchBoard_Mast SM
left outer join RE_ES_Estimations ES on SM.PrCode=ES.PrCode and
Sm.SBType=ES.SBType and SM.SBName=ES.SBName
Where SM.PrCode='PR004' and SM.SBType='SMDB'
) as s
PIVOT
(
Max(Qty)
FOR [SBName] IN (SB01, SB02, SB03)
)AS pvthere
and the result of my attempt looks like:
SB01 SB02 SB03
1 2 3
I have tried with MAX(Qty) but it is not working.
Thanks in advance.
You are almost there.
By adding ROW_NUMBER() OVER (PARTITION BY SBName ORDER BY Qty) rn to the source of PIVOT clause you get multiple rows for different SBName instead of one grouped row. Your query should look like:
SELECT SB01, SB02, SB03
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY SB.SBName ORDER BY Qty) rn,
SB.SBName,ISNULL(ES.Qty,0) Qty
FROM RE_ES_SwitchBoard_Mast SM
left outer join RE_ES_Estimations ES on SM.PrCode=ES.PrCode and
Sm.SBType=ES.SBType and SM.SBName=ES.SBName
Where SM.PrCode='PR004' and SM.SBType='SMDB'
) as s
PIVOT
(
Max(Qty)
FOR [SBName] IN (SB01, SB02, SB03)
)AS pvthere
A verifiable example here:
CREATE TABLE #sample
(
SBType varchar(MAX),
SBName varchar(MAX),
Qty int
)
INSERT INTO #sample VALUES ('SMDB','SB01',1)
INSERT INTO #sample VALUES ('SMDB','SB01',4)
INSERT INTO #sample VALUES ('SMDB','SB02',2)
INSERT INTO #sample VALUES ('SMDB','SB02',5)
INSERT INTO #sample VALUES ('SMDB','SB03',3)
INSERT INTO #sample VALUES ('SMDB','SB03',6)
SELECT SB01, SB02, SB03
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY SBName ORDER BY Qty) rn, SBName,ISNULL(Qty,0) Qty
FROM #sample
) as s
PIVOT
(
Max(Qty)
FOR [SBName] IN (SB01, SB02, SB03)
) AS pvthere
DROP TABLE #sample
Dynamic query is the only way to use varchar columns in pivot. Have a look at below code to get idea.
First step is to generate comma separated list of items for column you need to use in pivot.
Then you can use this generated list in dynamic query for pivot columns.
Note: For example purpose I have used temp table. Replace it with your actual table.
CREATE TABLE #temptable
(
SBType VARCHAR(20),
SBName VARCHAR(20),
Qty INT
)
INSERT INTO #temptable SELECT 'SMDB','SB01',1
INSERT INTO #temptable SELECT 'SMDB','SB01',4
INSERT INTO #temptable SELECT 'SMDB','SB02',2
INSERT INTO #temptable SELECT 'SMDB','SB02',5
INSERT INTO #temptable SELECT 'SMDB','SB03',3
INSERT INTO #temptable SELECT 'SMDB','SB03',6
SELECT * FROM #temptable
DECLARE #cols AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(SBName)
from #temptable
group by SBName
order by SBName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #cols
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT *
FROM
(
SELECT SBType,SBName,Qty,
row_number() over (partition by SBName order by Qty) as rn
FROM #temptable
) src
PIVOT
(
MIN(Qty)
FOR SBName IN (' + #cols + ')
) piv;'
EXEC(#query)
DROP TABLE #temptable

Is there a way to return more than 1 row in select without using existing tables

Simple question, just out of curiosity.
For example select 1,2,3 that will show table with one column and three rows.
Something like this: select values(1),(2),(3)
*with one select statement
An example for my comment in your post.
DECLARE #TABLE TABLE (ONE INT, TWO INT, THREE INT)
INSERT INTO #TABLE VALUES (1,2,3)
SELECT UP.COL, UP.VALUE
FROM #TABLE
UNPIVOT (VALUE FOR COL IN (ONE,TWO,THREE)) UP
Query:
DECLARE #t TABLE (i1 INT, i2 INT, i3 INT)
INSERT INTO #t VALUES (1, 2, 3)
SELECT t.*
FROM #t
CROSS APPLY (
VALUES(i1), (i2), (i3)
) t(value)
Output:
value
-----------
1
2
3
Additional info:
http://blog.devart.com/is-unpivot-the-best-way-for-converting-columns-into-rows.html
As it appears there is a simple code that I've been searching for:
select n from (values (1),(2),(3)) D(c);