How to pivot column values of the below table? - sql

TableA:
Brand Product
------------------
A X
A XX
A XXX
B Y
B YY
C Z
I need data as shown in Table below:
A B C
-------------------
X Y Z
XX YY NULL
XXX NULL NULL
How to do that in Sql Server 2008 ?

I dont beleive a PIVOT is what you are looking for here.
From what I can see you are looking at using the entries in order to generate the rows?
Also, PIVOTs make use of aggregate functions, so I cant see this happening.
What you can try, is something like
DECLARE #Table TABLE(
Brand VARCHAR(10),
Product VARCHAR(10)
)
INSERT INTO #Table SELECT 'A','X '
INSERT INTO #Table SELECT 'A','XX'
INSERT INTO #Table SELECT 'A','XXX'
INSERT INTO #Table SELECT 'B','Y'
INSERT INTO #Table SELECT 'B','YY'
INSERT INTO #Table SELECT 'C','Z'
;WITH Vals AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Brand ORDER BY (SELECT NULL)) RID
FROM #Table
)
, RIDs AS (
SELECT DISTINCT
RID
FROM Vals
)
SELECT vA.Product [A],
vB.Product [B],
vC.Product [C]
FROM RIDs r LEFT JOIN
Vals vA ON r.RID = vA.RID
AND vA.Brand = 'A' LEFT JOIN
Vals vB ON r.RID = vB.RID
AND vB.Brand = 'B' LEFT JOIN
Vals vC ON r.RID = vC.RID
AND vC.Brand = 'C'

I know it is a late entry, but here is a different approach to solve it:
DECLARE #Table TABLE(Brand VARCHAR(10), Product VARCHAR(10))
INSERT INTO #Table SELECT 'A','X '
INSERT INTO #Table SELECT 'A','XX'
INSERT INTO #Table SELECT 'A','XXX'
INSERT INTO #Table SELECT 'B','Y'
INSERT INTO #Table SELECT 'B','YY'
INSERT INTO #Table SELECT 'C','Z'
SELECT [A],[B],[C] FROM (
SELECT row_number() over (partition by brand order by product) rn,
Product, brand FROM #table
) as p
PIVOT(
MAX(product) for Brand in ([A],[B],[C])
)as pvt

Related

Join a table with comma-separated varchar values with another table's Id values in SQL Server

I have two tables, let's call them Users and Fruit.
Users
ID Name Fruit
-------------------
1 Bob 1,3
2 Jack 3
Fruit
ID Name
-------------
1 Apple
2 Orange
3 Grape
How does one join those two tables to fill a datatable with the users choice of fruit names?
Would I need to write a stored procedure with a loop?
I am rather new to SQL Server and would be glad for any help or to be pointed in the right direction.
Created Physical tables with sample data
CREATE TABLE TempUsers
( ID INT,
Name VARCHAR(100),
Fruit VARCHAR(100)
)
INSERT INTO TempUsers
SELECT 1,'Bob' ,'1,3' UNION ALL
SELECT 2,'Jack','3'
CREATE TABLE TempFruit
( ID INT,
Name VARCHAR(100))
INSERT INTO TempFruit
SELECT 1,'Apple' UNION ALL
SELECT 2,'Orange'UNION ALL
SELECT 3,'Grape'
Create A Table-valued-Function to retrive the fruit names as comma separated
CREATE FUNCTION [dbo].[udf_GetFruitNames]
(
#vc_String nvarchar(max)
)
RETURNS #OutTable TABLE
(
Reqdata nvarchar(max)
)
AS
BEGIN
DECLARE #Temp AS TABLE
(
DATA nvarchar(max)
)
INSERT INTO #Temp
SELECT #vc_String;
DECLARE #Temp1 AS TABLE
(
DATA nvarchar(max)
)
INSERT INTO #Temp1
SELECT
STUFF((SELECT DISTINCT ','+ Name FROM
(
SELECT ID,
Name
FROm TempFruit
WHERE ID IN ( SELECT
CAST(Split.a.value('.', 'nvarchar(1000)') AS INT) AS FruitId
FROM
( SELECT
CAST( '<S>'+ REPLACE(DATA,',','</S><S>')+'</S>' AS XML) AS FruitId
FROM #Temp f
)AS A
CROSS APPLY FruitId.nodes('S') AS Split(a))
) As dt FOR XML PATH ('')),1,1,'') As FruitName
INSERT INTO #OutTable
SELECT * FROM #Temp1
RETURN
END
Sql query
SELECT ID
,Name
,uf.Reqdata AS FruitNames
FROM TempUsers u
CROSS APPLY [dbo].[udf_GetFruitNames](u.Fruit) AS uf
Or
SELECT ID
,Name
,(SELECT Reqdata FROM [dbo].[udf_GetFruitNames](u.Fruit) ) AS FruitNames
FROM TempUsers u
Result
ID Name FruitNames
---------------------
1 Bob Apple,Grape
2 Jack Grape
First of all, you need to redesign your tables. There is need for junction table, which will hold which user is connected to what fruit. It is a N:N raletionship.
So, you should create such table:
FruitUser
UserId FruitId
1 1
1 3
2 3
UserId is FK to Users table, FruitId is FK to Fruits table and both of these columns form a compoiste primary key. That's a standard approach.
Then you can use simple join to get results:
select * from users u
join FruitUser fu on u.id = fu.userid
join Fruit f on f.id = fu.fruitId
Sample Data
DECLARE #Users AS TABLE(ID INt, Name VARCHAR(100),fruit VARCHAR(100))
INSERT INTO #Users
SELECT 1,'Bob' ,'1,3' UNION ALL
SELECT 2,'Jack','3'
DECLARE #Fruit AS TABLE(ID INt, Name VARCHAR(100))
INSERT INTO #Fruit
SELECT 1,'Apple' UNION ALL
SELECT 2,'Orange'UNION ALL
SELECT 3,'Grape'
Sql Script
;WITH CTE
AS
(
SELECT UserId,
UserName ,
CAST(Split.a.value('.', 'nvarchar(1000)') AS INT) AS FruitId
FROM
( SELECT u.ID AS UserId,
u.Name AS UserName ,
CAST( '<S>'+ REPLACE(fruit,',','</S><S>')+'</S>' AS XML) AS FruitId
FROM #Fruit f
INNER JOIN #Users u
ON u.ID=f.ID
)AS A
CROSS APPLY FruitId.nodes('S') AS Split(a)
)
SELECT Userid,
UserName,
FruitId,
ft.name AS FruitName
FROM CTE c
LEFT JOIN (SELECT * FROM #Fruit) AS Ft
ON ft.ID=c.FruitId
Result
Userid UserName FruitId FruitName
------------------------------------------
1 Bob 1 Apple
1 Bob 3 Grape
2 Jack 3 Grape
For SQL Server 2014 where you can't use STRING_SPLIT , you can split the varchar using XML like following.
;WITH cte
AS (SELECT id,
name,
fruitid
FROM (SELECT *,
Cast('<X>' + Replace(F.fruit, ',', '</X><X>')
+ '</X>' AS XML) AS xmlfilter
FROM users F)F1
CROSS apply (SELECT fdata.d.value('.', 'varchar(50)') AS FruitId
FROM f1.xmlfilter.nodes('X') AS fdata(d)) O)
SELECT *
FROM cte C
INNER JOIN fruit F
ON F.id = Cast(C.fruitid AS INT)
DEMO
You can achieve the desire result without changing anything with this query
SELECT u.Id, u.Name, f.name
FROM Users u
inner join Fruit f on f.ID IN (SELECT cast(value as int)FROM STRING_SPLIT(u.fruit, ','));
Since you have sql server 2014 you have various options like CLR, XML and number functions. Best one is CLR but it's complex. So you can use this XML code.
select * from
(SELECT ID, [name],LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS fruitid
FROM
(SELECT ID,[name],CAST('<XMLRoot><RowData>' + REPLACE(fruit,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x FROM {User table})t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)) u
inner join {fruit table} f on f.id = u.fruitid

SQL to display pivoted data

I have the following table:
and i want the following output displayed:
the above is basically the (quan*cst) for each day. Now I can acieve this by the following sql:
select t1.pid, isnull(b.m1,0) as day1sale, isnull(a.m2,0) as day2sale
from dbo.test1 t1
left join(select pid, sum(quan*cst) m1
from dbo.test1 where date='2017-05-01' group by pid) b on b.pid=t1.pid
left join (select pid, sum(quan*cst) m2
from dbo.test1 where date='2017-05-02' group by pid) a on a.pid=t1.pid
group by t1.pid,m2 ,m1
order by t1.pid
But i was wondering if there is a simpler way to do it without actually having to hard code the dates?
thanks in advance for the help!
Using a self join to get a list of distinct dates, the result set can be pivoted and the desired aggregates applied to each PID.
CREATE TABLE #TEMP_EXAMPLE
(
[DATE] DATE,
[ID] INT,
[PID] INT,
[QUAN] INT,
[CST] INT
)
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','1','1','2','3')
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','2','2','6','2')
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','3','3','5','1')
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','4','1','1','3')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','5','3','3','1')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','6','4','4','7')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','7','1','7','3')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','8','5','2','8')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','9','6','5','6')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','10','2','8','2')
SELECT ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY [DATE]) AS ID,*
INTO #TEMP_DYNAMIC_DATES
FROM (SELECT DISTINCT [DATE] FROM #TEMP_EXAMPLE ) AS X
SELECT * FROM #TEMP_DYNAMIC_DATES
SELECT PID,ISNULL(DAY1SALE,0) AS DAY1SALE,ISNULL(DAY2SALE,0) AS DAY2SALE FROM (
SELECT PID,SUM([QUAN] * [CST]) AS X, INDIC FROM (
SELECT A.*,
CASE WHEN B.ID = 1 THEN 'DAY1SALE'
WHEN B.ID = 2 THEN 'DAY2SALE'
END AS INDIC
FROM #TEMP_EXAMPLE AS A
JOIN #TEMP_DYNAMIC_DATES AS B
ON A.DATE = B.DATE
) AS X
GROUP BY PID,INDIC) AS O
PIVOT(SUM(X) FOR INDIC IN([DAY1SALE],[DAY2SALE])) AS PT

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

SQL Query using distinct and max

I have a dataset like:
type seqID text
A 1 Text1a
A 2 Text2a
A 3 Text3a
B 1 Text1b
B 2 Text2b
How do I get the row back by type with the highest seqID grouped by type? So in the above example I would want the row that has A, 3, Text3a and B, 2, Text2b returned.
SELECT *
FROM tmp t1
WHERE NOT EXISTS
(SELECT 1 FROM tmp t2 WHERE t1.type = t2.type AND t2.seqID > t1.seqID)
It shouldn't exists any other row with the same type and higher seqID.
You kind of need an ID, but since "Text" seems unique for this example
CREATE TABLE #TMP
(type VARCHAR(3), seqID INT, [text] varchar(256))
insert #TMP values ('A' , 1 , 'Text1a')
insert #TMP values ('A' , 2 , 'Text2a')
insert #TMP values ('A' , 3 , 'Text3a')
insert #TMP values ('B' , 1 , 'Text1b')
insert #TMP values ('B' , 2 , 'Text2b')
SELECT * FROM #TMP T
where [text] IN
(SELECT TOP 1 [text] FROM #TMP t2 WHERE t.type = t2.type ORDER BY t2.seqID DESC)
SELECT tbl.*
FROM
( SELECT type, MAX(seqID)
FROM tbl
GROUP BY type) maxes
WHERE
tbl.type= maxes.type AND
tbl.seqID= maxes.seqID
SELECT t.* FROM
(
SELECT type, MAX(seqID) as maxId
FROM Table
GROUP BY type
) m
INNER JOIN Table t ON m.maxId = t.seqId
Using CTE
;WITH maxIds(maxId)
AS
(
SELECT type, MAX(seqID) as maxId
FROM Table
GROUP BY type
)
SELECT t.* FROM
Table t
INNER JOIN maxIds m ON m.maxId = t.seqID
If you are on SQL Server 2005+, you could use a ranking function (more specifically, ROW_NUMBER()):
SELECT
type,
seqID,
text
FROM (
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY type ORDER BY seqID DESC)
FROM atable
) s
WHERE rnk = 1
create table #tlb1(
[type] VARCHAR(3), seqID INT, [text] varchar(max)
)
declare #type varchar(3), #text varchar(max);
declare #seqID int;
declare seq_cursor cursor for
select [type], max(seqID) from tbl group by [type]
open seq_cursor
fetch next from seq_cursor into #type,#seqID
while(##fetch_status=0)
begin
set #text= (select [text] from tbl where [type]=#type and seqID=#seqid);
insert into #tlb1 values (#type, #seqID,#text);
fetch next from seq_cursor into #type,#seqID
end
select * from #tlb1
close seq_cursor
deallocate seq_cursor
truncate table #tlb1
Try:
SELECT type, max(seqID),text
FROM 'db'
GROUP BY type
As easy as that.
EDITED solution. Consider this a psuedo-code (since I am not familiar with SQL server syntax):
SELECT a.type, a.seqID, a.text FROM table a
JOIN
(SELECT type, max(seqID) seqID FROM table GROUP BY type) b
ON a.seqID = b.seqID AND a.type=b.type

transact-sql question

Assume there were 100 records in tableA and tableA contained a column named 'price'.
How do I select the first-n record if where sum of price > a certain amount (e.g. 1000) without using cursor?
thanks
Top N implies some kind of order, which you did not supply, so I assumed any random order.
You can change this on the OVER clause of the ROW_NUMBER().
Try something like
DECLARE #Table TABLE(
Price FLOAT
)
INSERT INTO #Table SELECT 1
INSERT INTO #Table SELECT 11
INSERT INTO #Table SELECT 12
INSERT INTO #Table SELECT 15
INSERT INTO #Table SELECT 10
INSERT INTO #Table SELECT 65
INSERT INTO #Table SELECT 100
DECLARE #TotalPrice FLOAT
SELECT #TotalPrice = 100
;WITH Vals AS (
SELECT *,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RNR
FROM #Table
)
, Totals AS (
SELECT v.RNR,
SUM(vP.Price) TotalPrice
FROM Vals v LEFT JOIN
Vals vP ON v.RNR >= vP.RNR
GROUP BY v.RNR
)
, LimitValue AS (
SELECT TOP 1
RNR
FROM Totals
WHERE TotalPrice >= #TotalPrice
ORDER BY RNR
)
SELECT *
FROM Vals
WHERE RNR <= (
SELECT RNR
FROM LimitValue
)
select price from tableA
where price > 1000
limit n;
n - no. of records you want in result set
--
Cheers