Related
I using SQL Server 2008 R2 / 2014. I wish to find a SQL query that can do the following:
Rules:
Each [Group] must have [Number] 1 to 6 to be complete group.
[Name] in each [Group] must be unique.
Each row only can use 1 time.
Table before sorting is...
Name Number Group
---- ------ -----
A 1
B 6
A 123
C 3
B 4
C 23
D 45
D 4
C 56
A 12
D 56
After sorting, result I want is below or similar....
Name Number Group
---- ------ -----
A 1 1
C 23 1
D 45 1
B 6 1
A 123 2
D 4 2
C 56 2
A 12 3
C 3 3
B 4 3
D 56 3
What I tried before is to find a subgroup that have [Number] consist of 1-6 with below concatenate method...
SELECT *
FROM [Table1] ST2
WHERE
SUBSTRING((SELECT ST1.[Number] AS [text()]
FROM [Table1] ST1
-- WHERE ST1.[Group] = ST2.[Group]
ORDER BY LEFT(ST1.[Number],1)
FOR XML PATH ('')), 1, 1000) = '123456'
Maybe you should check ROW_NUMBER function.
select Name
, Number
, ROW_NUMBER () OVER(PARTITION BY Name ORDER BY Number) as Group
from [Table1]
If you have more than 6 rows with same NAME value then it will return more groups. You can filter additional groups out since you are interested in only 6 groups with unique values of NAME column.
I'm not sure if this can be done more simply or not, but here's my go at it...
Advanced warning, this requires some means of splitting strings. Since you're not on 2016, I've included a function at the beginning of the script.
The bulk of the work is a recursive CTE that builds the Name and Number columns into comma delimited groups. We then reduce our working set to only the groups where the numbers would create 123456, split the groups and use ROW_NUMBER() OVER... to identify them, and then select based on the new data.
Demo: http://rextester.com/NEXG53500
CREATE FUNCTION [dbo].[SplitStrings]
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
CREATE TABLE #temp
(
name VARCHAR(MAX),
number INT
)
INSERT INTO #temp
VALUES
('a',1),
('b',6),
('a',123),
('c',3),
('b',4),
('c',23),
('d',45),
('d',4),
('c',56),
('a',12),
('d',56);
/*** Recursively build groups based on information from #temp ***/
WITH groupFinder AS
(
SELECT CAST(name AS VARCHAR(MAX)) AS [groupNames], CAST(number AS VARCHAR(max)) AS [groupNumbers] FROM #temp
UNION ALL
SELECT
cast(CONCAT(t.[Name],',',g.[groupNames]) as VARCHAR(MAX)),
CAST(CONCAT(CAST(t.[Number] AS VARCHAR(max)),',',CAST(g.[groupNumbers] AS VARCHAR(max))) AS VARCHAR(max))
FROM #temp t
JOIN groupFinder g
ON
g.groupNames NOT LIKE '%' + t.name+'%'
AND g.[groupNumbers] NOT LIKE '%' + CAST(t.number/100 AS VARCHAR(10)) +'%'
AND g.[groupNumbers] NOT LIKE '%' + CAST(t.number/10 AS VARCHAR(10)) +'%'
AND g.[groupNumbers] NOT LIKE '%' + CAST(t.number%10 AS VARCHAR(10)) +'%'
)
/*** only get groups where the numbers form 123456 ***/
, groupPruner AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY [groupNames]) AS [rn] FROM groupFinder WHERE REPLACE([groupNumbers],',','') = '123456'
)
/*** split the name group and give it identifiers ***/
, nameIdentifier AS
(
SELECT g.*, c1.[item] AS [Name], ROW_NUMBER() OVER (PARTITION BY [rn] ORDER BY (SELECT NULL)) AS [rn1]
FROM groupPruner g
CROSS APPLY splitstrings(g.groupnames,',') c1
)
/*** split the number group and give it identifiers ***/
, numberIdentifier AS
(
SELECT g.*, c1.[item] AS [Number], ROW_NUMBER() OVER (PARTITION BY [rn], [rn1] ORDER BY (SELECT NULL)) AS [rn2]
FROM nameIdentifier g
CROSS APPLY splitstrings(g.groupNumbers,',') c1
)
SELECT [Name], [Number], [rn] AS [Group]
--,groupnames, groupNumbers /*uncomment this line to see the groups that were built*/
FROM numberIdentifier
WHERE rn1 = rn2
ORDER BY rn, rn1
DROP TABLE #temp
My pricelist table looks like this:
ItemCode VendorCode UnitCost StartingDate
333 362 2.31 2016-08-19 00:00:00.0
333 362 2.16 2018-02-22 00:00:00.0
444 362 12.96 2014-01-09 00:00:00.0
444 362 13.10 2015-01-09 00:00:00.0
444 430 13.05 2017-04-01 00:00:00.0
444 550 13.30 2018-02-01 00:00:00.0
I would like to have query result following:
333:(362,2.16,2018-02-22)
444:(362,13.10,2015-01-09),(430,13.05,2017-04-01),(550,13.30,2018-02-01)
So all different Vendors and their prices should be listed and only latest by date.
I got this far:
SELECT
Pricelist.ItemCode + ': '+ temp.data
FROM Pricelist
INNER JOIN (SELECT p1.ItemCode,
STUFF((SELECT '; ' + p2.VendorCode
FROM Pricelist p2
WHERE p2.ItemCode = p1.ItemCode
ORDER BY VendorCode
FOR XML PATH('')), 1, 1, '') AS data
FROM Pricelist p1) as temp
ON Pricelist.ItemCode = temp.ItemCode
GROUP BY Pricelist.ItemCode, temp.data
ORDER BY 1
But not even close to the result I need.
I would use row_number() function :
select concat(itemcode, ':',
stuff( ( select top (1) with ties ',(' +concat(VendorCode, ',', UnitCost ,',', cast(StartingDate as date)) +')'
from Pricelist
where itemcode = p.itemcode
order by row_number() over (partition by VendorCode order by StartingDate desc)
for xml path('')
), 1, 1, ''
))
from Pricelist p
group by itemcode;
Try with this query:
create table #t (ItemCode int, VendorCode int, UnitCost decimal (10,2), StartingDate datetime)
insert into #t values
(333,362, 2.31,'2016-08-19 00:00:00.0'),
(333,362, 2.16,'2018-02-22 00:00:00.0'),
(444,362,12.96,'2014-01-09 00:00:00.0'),
(444,362,13.10,'2015-01-09 00:00:00.0'),
(444,430,13.05,'2017-04-01 00:00:00.0'),
(444,550,13.30,'2018-02-01 00:00:00.0')
;with tr1 as (
select
convert(varchar(100),ItemCode) + ':' as ItemCode,
'(' + convert(varchar(100),VendorCode) + ',' + convert(varchar(100),UnitCost) + ',' + convert(varchar(19),StartingDate,121) + ')' as Vals,
row_number() over (partition by ItemCode,VendorCode order by StartingDate desc) rn
from #t
)
select distinct ItemCode,
stuff((
select ',' + Vals
from tr1 b
where b.ItemCode=tr1.ItemCode
and rn=1
for xml path ('')
),1,1,'')
from tr1
where rn=1
First grouping the itemcodes, and then linking that to a string with the vendor details might be quite performant.
Linking those itemcodes to an OUTER APPLY with a FOR XML works well.
For example :
declare #Pricelist table (ItemCode int, VendorCode int, UnitCost decimal (10,2), StartingDate datetime)
insert into #Pricelist values
(333,362,02.31,'2016-08-19T00:01:00'),
(333,362,02.16,'2018-02-22T00:02:00'),
(444,362,12.96,'2014-01-09T00:03:00'),
(444,362,13.10,'2015-01-09T00:04:00'),
(444,430,13.05,'2017-04-01T00:05:00'),
(444,550,13.30,'2018-02-01T00:06:00');
select concat(itemcode,':',stuff(x.details,1,1,'')) as ItemVendorDetails
from (select distinct itemcode from #Pricelist) i
outer apply
(
select top 1 with ties
concat(',(',VendorCode,',',UnitCost,',',convert(date,StartingDate),')')
from #Pricelist p
where p.ItemCode = i.ItemCode
order by row_number() over (partition by ItemCode, VendorCode order by StartingDate desc)
for xml path('')
) x(details);
Result:
ItemVendorDetails
------------------------------------------------------------------------
333:(362,2.16,2018-02-22)
444:(362,13.10,2015-01-09),(430,13.05,2017-04-01),(550,13.30,2018-02-01)
The following query works with the sample data provided:
;WITH VendorPerItemCTE AS (
SELECT ItemCode,
VendorCode,
UnitCost,
StartingDate,
ROW_NUMBER() OVER (PARTITION BY ItemCode, VendorCode
ORDER BY StartingDate DESC) AS seq
FROM PriceList
)
SELECT CAST(ItemCode AS VARCHAR(12)) + ':' + STUFF(ItemData , 1, 1, '')
FROM (
SELECT DISTINCT p.ItemCode,
(SELECT ', (' + CAST(VendorCode AS VARCHAR(10)) + ', ' +
CAST(UnitCost AS VARCHAR(10)) + ', ' +
CONVERT(VARCHAR(12), StartingDate, 102 ) +
')'
FROM VendorPerItemCTE AS c
WHERE p.ItemCode = c.ItemCode AND c.seq = 1
FOR XML PATH('')) AS ItemData
FROM PriceList AS p) AS t
Demo here
I have 2 tables Orders and Order Details table. I want to combine both tables data with header and details in a dataset. When I join these two tables i get combined data but i want a pattern to pull the header first and then the details next without using loops or Cursors.
Here is the example below described.:
ORDER TABLE:
OrderID Orderdate customerF_name customerL_name OrderAmount
1001 2/21/2018 abc def $3,025
1002 2/21/2018 ghi jkl $7,226
1003 2/21/2018 mno pqr $5,752
ORDER DETAILS TABLE:
OrderID Item Item_Desc Item_Amount Quantity
1002 sofa leather sofa. $1,900 1
1002 bed bedset $800 1
1002 desser desser $780 1
1002 dining 120" table.. $3,746 1
1001 cocktail cocktail tbl set $1,780 1
1001 Bedset double bed set $1,245 1
THE EXPECTED OUTPUT:
1001,2/21/2018,abc,def,3,025
1001,cocktail,cocktail tbl set,1,780,1
1001,Bedset,double bed set,1,245,1
1002,2/21/2018,ghi,jkl,7,226,1
1002,sofa,leather sofa.,1,900,1
1002,bed,bedset,800,1
1002,desser,desser,780,1
1002,dining,120" table..,3,746,1
You might have observed the pattern in the output. The first row for each OrderID is pulled from the Header table and next followed by the details for the same orderId. This pattern will repeat until the last row of the Header table.
I am actully trying to create a query to produce a flat file in my SSIS ETL process and send the flat file to the users who needed.
Could some one please help me how to write MS-SQL query to achieve the final result.
Thank you so much for your help.
Select orderid,col2, col3,...,col100 FROM
(
SELECT orderid,col2, col3,...,col100,1 as Seq from header_table
UNION ALL
SELECT orderid,col2, col3,...,col100,2 as Seq from detail_table
) ORDER BY orderid,Seq
A union query would probably work. I'll type something out to show the general idea. It won't be perfect.
select orderId + ',' + firstName + ','
+ LastName + ',' + cast(orderAmount as varchar(10)) row
union
select orderId + ',' + item + ',' + itemDescription + ','
+ Cast(ItemAmount as varchar (50) )+ ',' + cast (quantity as varchar(10)) row
order by row
Try the following:
--ORDER TABLE:
Declare #order table(OrderID int, Orderdate varchar(10), customerF_name varchar(100), customerL_name varchar(100), OrderAmount money)
insert into #order select 1001, '2/21/2018', 'abc', 'def', '$3,025'
insert into #order select 1002, '2/21/2018', 'ghi', 'jkl', '$7,226'
insert into #order select 1003, '2/21/2018', 'mno', 'pqr', '$5,752'
--ORDER DETAILS TABLE:
declare #order_detail table (OrderID int, Item varchar(100), Item_Desc varchar(100), Item_Amount money, Quantity int)
insert into #order_detail select 1002 ,'sofa ', 'leather sofa. ', '$1,900' , 1
insert into #order_detail select 1002 ,'bed ', 'bedset ', ' $800' , 1
insert into #order_detail select 1002 ,'desser ', 'desser ', ' $780' , 1
insert into #order_detail select 1002 ,'dining ', '120" table.. ', '$3,746' , 1
insert into #order_detail select 1001 ,'cocktail ', 'cocktail tbl set ', '$1,780' , 1
insert into #order_detail select 1001 ,'Bedset ', 'double bed set ', '$1,245' , 1
declare #output_table table (Col1 varchar(100), Col2 varchar(100), Col3 varchar(100), Col4 varchar(100) ,Col5 varchar(100))
insert into #output_table
select * from #order where OrderId in (select distinct o.OrderId from #order o join #order_detail od on od.OrderId = o.OrderId)
insert into #output_table
select * from #order_detail where OrderId in (select distinct o.OrderId from #order o join #order_detail od on od.OrderId = o.OrderId)
select * from #output_table order by 1
Try this:
select * from (
select OrderId, CAST(orderdate as varchar(20)) [orderdate], customerF_name, customerL_name, CAST(orderAmount as varchar(15)) [orderAmount] from ORDER_TABLE
union all
select orderId, item, item_desc, CAST(item_amount as varchar(15)), CAST(quantity as varchar(5)) from ORDER_DETAILS_TABLE
) a order by orderId, [orderDate]
Because of that you have different datatypes in respective columns, you have to use casting :)
Rather than hack up the data like that why not just do a join
select o.*, d.Item, d.Desc, d.Amount, d.Quantity
from order o
join detail d
on o.OrderID = d.OrderID
order
by o.OrderID, d.Item
I have created 3 table
table 1 "Sale" that saved date of user sale
table 2 "ProductFactor" that saved factorid and productid in wich factoreid
tabel 3 "Product" that saved productid and product name
I want select in 3 table for this result:
show user factorid +price+ saledate + any product name that avalibe in this factorid
but when do it, for example when in one factorid that have 3 productid, is show this:
date factoreid name price
2013-09-25 1 1 a 2000
2013-09-25 1 1 b 3000
2013-09-25 2 2 c 4000
2013-09-25 3 3 d 3500
2013-09-25 1 1 e 8000
I want show this:
date factoreid name price sumprice
2013-09-25 1 1 a,b,e a price,b price, c price sum of this 3 product name
2013-09-25 2 2 c 4000
2013-09-25 3 3 d 3500 my code is:
CREATE PROCEDURE [dbo].[GetUserSaleReport]
#CurrentUserId uniqueidentifier
AS BEGIN
SELECT dbo.Sale.SaleDate,
dbo.ProductFactor.FactoreId,
dbo.Product.ProductName,
dbo.Product.Price
FROM Sale
INNER JOIN ProductFactor
ON dbo.Sale.FactoreId=dbo.ProductFactor.FactoreId
INNER JOIN dbo.Product
ON dbo.ProductFactor.ProductId=dbo.Product.ProductId
WHERE dbo.Sale.UserId = #CurrentUserId
AND dbo.Sale.FactoreId=dbo.ProductFactor.FactoreId
AND dbo.ProductFactor.ProductId=dbo.Product.ProductId
ORDER BY dbo.Sale.SaleDate
END
I dont have all three tables but assuming all this data was coming from one table you could do something like this... You can apply the same login after you have joined all the tables and have got all the required data in one place
SELECT DISTINCT [DATE], Factoreid, STUFF(T2.NameList, 1, 2, '') AS name, STUFF(T3.PriceList, 1, 2, '') AS price, SUM(Price) PriceSUM
FROM TableName t
OUTER APPLY (
SELECT ', ' + name [text()]
FROM TableName
WHERE Factoreid = t.Factoreid
FOR XML PATH('')
) T2(NameList)
OUTER APPLY (
SELECT ', ' + CAST(price AS VARCHAR) [text()]
FROM TableName
WHERE Factoreid = t.Factoreid
FOR XML PATH('')
) T3(PriceList)
GROUP BY [DATE], Factoreid, STUFF(T2.NameList, 1, 2, ''), STUFF(T3.PriceList, 1, 2, '')
I have tried tweaked the code I think it will work try this
CREATE PROCEDURE [dbo].[GetUserSaleReport]
#CurrentUserId uniqueidentifier
AS BEGIN
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#Temptable') IS NOT NULL
DROP TABLE #Temptable
SELECT dbo.Sale.SaleDate AS [DATE],
dbo.ProductFactor.FactoreId AS Factoreid,
dbo.Product.ProductName AS Name,
dbo.Product.Price AS Price
INTO #Temptable
FROM Sale
INNER JOIN ProductFactor
ON dbo.Sale.FactoreId=dbo.ProductFactor.FactoreId
INNER JOIN dbo.Product
ON dbo.ProductFactor.ProductId=dbo.Product.ProductId
WHERE dbo.Sale.UserId = #CurrentUserId
AND dbo.Sale.FactoreId=dbo.ProductFactor.FactoreId
AND dbo.ProductFactor.ProductId=dbo.Product.ProductId
ORDER BY dbo.Sale.SaleDate
SELECT DISTINCT [DATE], Factoreid, STUFF(T2.NameList, 1, 2, '') AS name, STUFF(T3.PriceList, 1, 2, '') AS price, SUM(Price) PriceSUM
FROM #Temptable t
OUTER APPLY (
SELECT ', ' + Name [text()]
FROM #Temptable
WHERE Factoreid = t.Factoreid
FOR XML PATH('')
) T2(NameList)
OUTER APPLY (
SELECT ', ' + CAST(price AS VARCHAR) [text()]
FROM #Temptable
WHERE Factoreid = t.Factoreid
FOR XML PATH('')
) T3(PriceList)
GROUP BY [DATE], Factoreid, STUFF(T2.NameList, 1, 2, ''), STUFF(T3.PriceList, 1, 2, '')
IF OBJECT_ID('tempdb..#Temptable') IS NOT NULL
DROP TABLE #Temptable
SET NOCOUNT OFF;
END
I have a table like
ID option
1 optionA
1 optionB
1 optionC
1 optionD
And I want a result like:
ID A B C D
1 optionA optionB optionC optionD
What is the best way to do this?
Query which i have tried is
select * from TableName PIVOT (option for ID = 2674 )) as abc
this will not work since PIVOT expects aggregated function..
I have also tried COALESCE like this
declare #t table(num VARCHAR(100))
insert into #t
select choice FROM QuestionAnswers where QuestionID=2674
select num from #t
declare #s varchar(8000)
select #s = COALESCE(#s + ',', '') + num
from #t
exec('select '+#s)
but this doesn't work as well..
This type of data transformation is known as a pivot. In SQL Server 2005+ there is a function that will perform this data rotation for you. However, there are many ways that you can perform this data transformation.
Here is a PIVOT query that will work with your sample data:
select *
from
(
select id, [option], right([option], 1) col
from yourtable
) src
pivot
(
max([option])
for col in (a, b, c, d)
) piv
See SQL Fiddle with Demo.
This can also be performed using an aggregate function with a CASE expression:
select id,
max(case when col = 'a' then [option] else null end) a,
max(case when col = 'b' then [option] else null end) b,
max(case when col = 'c' then [option] else null end) c,
max(case when col = 'd' then [option] else null end) d
from
(
select id, [option], right([option], 1) col
from yourtable
) src
group by id
See SQL Fiddle with Demo.
You can perform multiple joins on your table:
select a.id,
a.[option] a,
b.[option] b,
c.[option] c,
d.[option] d
from yourtable a
left join yourtable b
on a.id = b.id
and right(b.[option], 1) = 'b'
left join yourtable c
on a.id = c.id
and right(c.[option], 1) = 'c'
left join yourtable d
on a.id = d.id
and right(d.[option], 1) = 'd'
where right(a.[option], 1) = 'a'
See SQL Fiddle with Demo
Lastly, this can be done using dynamic sql if the values to be turned into columns is unknown:
DECLARE #colsName AS NVARCHAR(MAX),
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #colsName = STUFF((SELECT distinct ', ' + QUOTENAME(right([option], 1)) +' as '+ right([option], 1)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #cols = STUFF((SELECT distinct ', ' + QUOTENAME(right([option], 1))
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, ' + #colsName + ' from
(
select id, [option], right([option], 1) col
from yourtable
) x
pivot
(
max([option])
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
The result of all queries is:
| ID | A | B | C | D |
----------------------------------------------
| 1 | optionA | optionB | optionC | optionD |
CREATE TABLE Product(Cust VARCHAR(25), Product VARCHAR(20), QTY INT)
GO
-- Inserting Data into Table
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',2)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','SODA',6)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','MILK',1)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','BEER',12)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','MILK',3)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','BEER',24)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',3)
GO
-- Selecting and checking entires in table
SELECT *
FROM Product
GO
-- Pivot Table ordered by PRODUCT
SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT
GO
-- Pivot Table ordered by CUST
SELECT CUST, VEG, SODA, MILK, BEER, CHIPS
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)) AS pvt
ORDER BY CUST
GO
-- Unpivot Table ordered by CUST
SELECT CUST, PRODUCT, QTY
FROM
(
SELECT CUST, VEG, SODA, MILK, BEER, CHIPS
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT
( SUM(QTY) FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)) AS pvt) p
UNPIVOT
(QTY FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)
) AS Unpvt
GO
-- Clean up database
DROP TABLE Product
GO
Ref http://blog.sqlauthority.com/2008/06/07/sql-server-pivot-and-unpivot-table-examples/
Edited
DECLARE #Product TABLE (ID int, _option varchar(10) )
-- Inserting Data into Table
INSERT INTO #Product
(
ID,
_option
)
SELECT 1, 'optionA' UNION ALL
SELECT 1, 'optionB' UNION ALL
SELECT 1, 'optionC' UNION ALL
SELECT 1, 'optionD'
SELECT
optionA, optionB, optionC, optionD
FROM (
SELECT ID, _option
FROM #Product) up
PIVOT (SUM(ID) FOR _option IN (optionA, optionB, optionC, optionD)) AS pvt