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
Related
There are 2 SQL Server tables:
Products:
Name Status Code
------------------------------------
Product 1 1001, 1003
Product 2 1001, 1005, 1006
Status:
Code Description
------------------------------------
1001 State A
1003 State B
1005 State C
1006 State D
...
I wish to get something like:
Product Status
---------------------------------------------
Product 1 State A, State B
Product 2 State A, State C, State D
You can use a query like below:
See working demo
create table Products (Name varchar(100), [Status Code] varchar(100));
insert into Products values
('Product 1', '1001, 1003')
,('Product 2', '1001, 1005, 1006');
create table [Status] (Code varchar(100), Description varchar(100));
insert into [Status] values
('1001', 'State A')
,('1003', 'State B')
,('1005', 'State C')
,('1006', 'State D')
; WITH X AS
(
SELECT
P1.Name,
S.Description
FROM
(
SELECT *,
cast('<X>'+replace(P.[Status Code],',','</X><X>')+'</X>' as XML) AS xmlprods FROM Products P
)P1
CROSS APPLY
(
SELECT fdata.D.value('.','varchar(100)') AS splitdata
FROM P1.xmlprods.nodes('X') AS fdata(D)) O
LEFT JOIN [Status] S
ON S.Code= LTRIM(RTRIM(O.splitdata ))
)
SELECT
Name,
Description= STUFF((
SELECT ',' + Description FROM x AS x2
WHERE x2.Name = x.Name
ORDER BY Name FOR XML PATH,
TYPE).value(N'.[1]',N'varchar(max)'), 1, 1, '')
FROM
X
GROUP BY Name
You can try this. I used a temporary table but you can use any means you want
SELECT p.Product , p.Status , s.Description INTO #tmp FROM (
SELECT Name Product, TRIM(value ) Status FROM tblProduct CROSS
APPLY STRING_SPLIT(TRIM(StatusCode), ',') ) p JOIN tblStatus s ON
s.Code = p.Status
select distinct Product
, stuff((
select ',' + tmp2.Description
from #tmp tmp2
where tmp2.Product = tmp1.Product
order by tmp2.Product
for xml path('')
) ,1,1,'') as StatusCode from #tmp tmp1 group by Product
Below Query will help to make any comma separated to list.
SELECT A.OtherID,
Split.a.value('.', 'VARCHAR(100)') AS Data
FROM
(
SELECT OtherID,
CAST ('<M>' + REPLACE(Data, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM Table1
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
let say i have a table like below
Food(FoodID,FoodName)
Package(PackageID,PackageName,FoodID)
When i do
SELECT P.PackageID,F.FoodName FROM FOOD F inner join PACKAGE P on F.FoodID = P.FoodID
it returns data like following
PackageID FoodName
1 Mango
1 Apple
1 Tacos
but i want to get it like following
PackageID FoodName
1 Mango,Apple,Tacos
How to do it
CREATE TABLE #FOOD (FoodID INT, FoodName VARCHAR(100) )
CREATE TABLE #PACKAGE (PackageID INT, FoodID INT)
INSERT INTO #FOOD
SELECT 1, 'Mango'
Union
SELECT 2, 'Apple'
Union
SELECT 3, 'Tacos'
INSERT INTO #PACKAGE
SELECT 1, 1
Union
SELECT 1, 2
Union
SELECT 1, 3
SELECT F1.PackageID,
STUFF(( SELECT ', ' + F2.FoodName
FROM
(SELECT P.PackageID,F.FoodName
FROM #FOOD F
INNER JOIN #PACKAGE P
ON F.FoodID = P.FoodID) F2
WHERE F1.PackageID = F2.PackageID
FOR XML PATH('')
), 1, 2, '') [Attributes]
FROM (SELECT P.PackageID,F.FoodName
FROM #FOOD F
INNER JOIN #PACKAGE P
ON F.FoodID = P.FoodID) F1
GROUP BY F1.PackageID
I have complicated query which works pretty good (MS SQL 2012). But it makes a mistake when sum up same price items. It count them correctly but only takes price once instead of taking them as a count number. It only appears when same item has same price and from same country. Here is my query ;
CREATE TABLE #ITEMS(ID INT,NAME VARCHAR(30))
INSERT INTO #ITEMS
SELECT 1, 'laptop'
UNION ALL
SELECT 2, 'phone'
UNION ALL
SELECT 3, 'playstation'
UNION ALL
SELECT 4, 'MacBook'
CREATE TABLE #Country(ID INT,NAME VARCHAR(30))
INSERT INTO #Country
SELECT 1, 'England'
UNION ALL
SELECT 2, 'Sweden'
UNION ALL
SELECT 3, 'Russia'
UNION ALL
SELECT 4, 'Italy'
CREATE TABLE [#Pre-Request](Id INT, countryId INT, ItemId INT)
INSERT INTO [#Pre-Request]
SELECT 1,1,3
UNION ALL
SELECT 2,2,1
UNION ALL
SELECT 3,2,2
UNION ALL
SELECT 4,3,3
UNION ALL
SELECT 5,3,3
UNION ALL
SELECT 6,2,3
CREATE TABLE #Offers(Id INT, PRICE VARCHAR(50))
INSERT INTO #Offers
SELECT 18,'257$'
UNION ALL
SELECT 19,'151$'
UNION ALL
SELECT 20,'424$'
UNION ALL
SELECT 21,'433$'
UNION ALL
SELECT 22,'151$'
CREATE TABLE #Request(Id INT, preReqId INT, requestStatus INT,winOfferId INT)
INSERT INTO #Request
SELECT 44, 1, 3, 18
UNION ALL
SELECT 11, 2, 4, 21
UNION ALL
SELECT 53, 3, 4, 20
UNION ALL
SELECT 87, 4, 3, 22
UNION ALL
SELECT 43, 5, 3, 19
UNION ALL
SELECT 43, 6, 2, Null
;WITH CTE AS
(
SELECT DISTINCT I.NAME ITEMNAME,C.NAME COUNTRYNAME
,CAST(REPLACE(TAB.PRICE,'$','')AS INT)PRICE
,COUNT(CASE WHEN TAB.PRICE IS NOT NULL THEN I.NAME END) OVER(PARTITION BY C.NAME,I.NAME) CNTITEM
FROM [#Pre-Request] PR
LEFT JOIN #Items I ON PR.ITEMID=I.ID
LEFT JOIN #COUNTRY C ON PR.COUNTRYID = C.ID
OUTER APPLY
(
SELECT R.preReqId,R.winOfferId,O.PRICE
FROM #Request R
JOIN #Offers O ON R.winOfferId=O.Id
WHERE PR.ID=R.preReqId
)TAB
UNION
-- Used to select Item name and country that are not in Pre-request table and other tables
SELECT I.NAME ,C.NAME ,NULL,0
FROM #Items I
CROSS JOIN #COUNTRY C
)
,CTE2 AS
(
-- Find the sum for number of items
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME,
SUM(PRICE)PRICE
FROM CTE
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
,CTE3 AS
(
-- Find the sum of PRICE
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME--,CNTITEM
,SUM(CNTITEM)CNTITEM
FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,CNTITEM
FROM CTE
)TAB
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
SELECT C2.*,C3.CNTITEM,
CAST(C3.CNTITEM AS VARCHAR(20))+'x'+' ' + CAST(C2.PRICE AS VARCHAR(20))+'$' NEWCOL
INTO #NEWTABLE
FROM CTE2 C2
JOIN CTE3 C3 ON C2.COUNTRYNAME=C3.COUNTRYNAME AND C2.ITEMNAME=C3.ITEMNAME
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + ITEMNAME + ']', '[' + ITEMNAME + ']')
FROM (SELECT DISTINCT ITEMNAME FROM #NEWTABLE WHERE ITEMNAME<>'TOTAL') PV
ORDER BY ITEMNAME
-- Since we need Total in last column, we append it at last
SELECT #cols += ',[Total]'
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT COUNTRYNAME,' + #cols + ' FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,ISNULL(NEWCOL,''0x 0$'')NEWCOL
FROM #NEWTABLE
) x
PIVOT
(
MIN(NEWCOL)
FOR ITEMNAME IN (' + #cols + ')
) p
ORDER BY CASE WHEN (COUNTRYNAME=''Total'') THEN 1 ELSE 0 END,COUNTRYNAME'
EXEC SP_EXECUTESQL #query
and here is result ;
As you can see there are 2 "playstation" from "Russia" it takes correct count (2x) but only take 1 price "151$" (normally it must be 302$). How can I fix this without making major changes from query? Thank you.
I think this does what you want. The problem is in your first CTE where you do the item count and get a distinct on the ItemName, CountryName, and Price.
Instead of getting a distinct, do a group by as shown below and SUM the price.
SELECT I.NAME ITEMNAME, C.NAME COUNTRYNAME
,SUM(CAST(REPLACE(TAB.PRICE,'$','')AS INT))PRICE
,COUNT(CASE WHEN TAB.PRICE IS NOT NULL THEN 1 ELSE NULL END) CNTITEM
FROM [#Pre-Request] PR
LEFT JOIN #Items I ON PR.ITEMID=I.ID
LEFT JOIN #COUNTRY C ON PR.COUNTRYID = C.ID
OUTER APPLY
(
SELECT R.preReqId,R.winOfferId,O.PRICE
FROM #Request R
JOIN #Offers O ON R.winOfferId=O.Id
WHERE PR.ID=R.preReqId
)TAB
GROUP BY
I.NAME
,C.NAME
EDIT:
Here are the results I get:
Here's all of your code starting from the CTEs:
;WITH CTE AS
(
SELECT I.NAME ITEMNAME, C.NAME COUNTRYNAME
,SUM(CAST(REPLACE(TAB.PRICE,'$','')AS INT))PRICE
,COUNT(CASE WHEN TAB.PRICE IS NOT NULL THEN 1 ELSE NULL END) CNTITEM
FROM [#Pre-Request] PR
LEFT JOIN #Items I ON PR.ITEMID=I.ID
LEFT JOIN #COUNTRY C ON PR.COUNTRYID = C.ID
OUTER APPLY
(
SELECT R.preReqId,R.winOfferId,O.PRICE
FROM #Request R
JOIN #Offers O ON R.winOfferId=O.Id
WHERE PR.ID=R.preReqId
)TAB
GROUP BY
I.NAME
,C.NAME
UNION
-- Used to select Item name and country that are not in Pre-request table and other tables
SELECT I.NAME ,C.NAME ,NULL,0
FROM #Items I
CROSS JOIN #COUNTRY C
)
,CTE2 AS
(
-- Find the sum for number of items
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME,
SUM(PRICE)PRICE
FROM CTE
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
,CTE3 AS
(
-- Find the sum of PRICE
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME--,CNTITEM
,SUM(CNTITEM)CNTITEM
FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,CNTITEM
FROM CTE
)TAB
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
SELECT C2.*,C3.CNTITEM,
CAST(C3.CNTITEM AS VARCHAR(20))+'x'+' ' + CAST(C2.PRICE AS VARCHAR(20))+'$' NEWCOL
INTO #NEWTABLE
FROM CTE2 C2
JOIN CTE3 C3 ON C2.COUNTRYNAME=C3.COUNTRYNAME AND C2.ITEMNAME=C3.ITEMNAME
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + ITEMNAME + ']', '[' + ITEMNAME + ']')
FROM (SELECT DISTINCT ITEMNAME FROM #NEWTABLE WHERE ITEMNAME<>'TOTAL') PV
ORDER BY ITEMNAME
-- Since we need Total in last column, we append it at last
SELECT #cols += ',[Total]'
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT COUNTRYNAME,' + #cols + ' FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,ISNULL(NEWCOL,''0x 0$'')NEWCOL
FROM #NEWTABLE
) x
PIVOT
(
MIN(NEWCOL)
FOR ITEMNAME IN (' + #cols + ')
) p
ORDER BY CASE WHEN (COUNTRYNAME=''Total'') THEN 1 ELSE 0 END,COUNTRYNAME'
EXEC SP_EXECUTESQL #query
Assuming I have a query returning
OrderID Date Product Count
1 2015-01-01 "Bread" 1
2 2015-02-02 "Water" 2
2 2015-02-02 "Bread" 7
2 2015-02-02 "Soap" 3
How can I make this query to return 2 records (group by ID) concatenating multiple columns
OrderID Date Detail
1 2015-01-01 "Bread (1)"
2 2015-02-02 "Bread (7), Soap (3), Water (2)"
Where I concatenate:
Product Names and their Count value showing (in brackets)
Order by Product Name
into a "Detail" NVarchar result ??
you didn't mention if date is different for same orderid then what will happen.
Try this,
Declare #orders TABLE ([OrderID] INT,[Date] DATE,[Product] VARCHAR(10),[Count] INT)
INSERT INTO #orders
VALUES (1,'2015-01-01','Bread',1),
(2,'2015-02-02','Water',2),
(2,'2015-02-02','Bread',7),
(2,'2015-02-02','Soap',3),
(2,'2015-03-02','Soap',3)
;WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY orderid
,[date] ORDER BY [date]
) rn
FROM #orders
)
SELECT OrderID
,[date]
,stuff((
SELECT ',' + Product + ' ( ' + CONVERT(VARCHAR,[Count]) + ' ) '
FROM cte
WHERE orderid = a.OrderID
AND [Date] = a.[Date]
FOR XML path('')
), 1, 1, '') [details]
FROM CTE A
WHERE rn = 1
I'll let you handle the trailing comma removal.
CREATE TABLE orders ([OrderID] INT,[Date] DATE,[Product] VARCHAR(10),[Count] INT)
INSERT INTO orders
VALUES (1,'2015-01-01','Bread',1),
(2,'2015-02-02','Water',2),
(2,'2015-02-02','Bread',7),
(2,'2015-02-02','Soap',3)
SELECT
OrderID,
[Date],
(
SELECT DISTINCT
[Product] + '(' + CONVERT(VARCHAR,[Count]) + '), '
FROM
orders o1
WHERE o1.OrderID = o.OrderID
FOR XML PATH('')
) Detail
FROM orders o
GROUP BY OrderID, [Date]
I have a table say ProjectMaster:
Id ProjectName
1 A
2 B
3 C
another table ProjectMeter
Id ProjectId MeterNumber
1 1 #0001
2 1 #0002
3 1 #0003
4 2 #0004
5 2 #0005
6 3 #0006
I wish to have following output
ProjectName MeterNumbers
A #0001, #0002, #0003
B #0004, #0005
C #0006
I tried this and this, but unable to solve my problem.
I cannot use a table variable.
I have a already written Stored Procedure and it brings data from many joined tables. ProjectMaster also happens to be joined in one of these tables. Now am required to fetch data from ProjectMeter, such that, each row has concatenated ProjectMeter.MeterNumber corresponding to the ProjectId in that column.
right now, I get concatenated list of all meternumbers in all the rows.
I cannot use CURSOR, TABLE variable , Temp TABLE
( I hope still something can be done to my cause)
please help.....
Try this:
SELECT projectname, STUFF((SELECT distinct ', ' + meternumber
from projectmeter m
where p.id = m.projectid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') MeterNumbers
from projectmaster p
See SQL Fiddle with Demo
DECLARE #ProjectMaster AS TABLE
(
ID INT IDENTITY(1, 1) ,
ProjectName VARCHAR(2)
)
DECLARE #ProjectMeter AS TABLE
(
ID INT IDENTITY(1, 1) ,
ProjectID INT ,
MeterNumber VARCHAR(50)
)
INSERT INTO #ProjectMaster
( ProjectName )
VALUES ( 'A' )
INSERT INTO #ProjectMeter
( ProjectID, MeterNumber )
VALUES ( 1, '#0001' )
INSERT INTO #ProjectMeter
( ProjectID, MeterNumber )
VALUES ( 1, '#0002' )
SELECT pMaster.ID, STUFF(( SELECT ',' + MeterNumber
FROM #ProjectMeter
FOR
XML PATH('')
), 1, 1, '') AS 'Concat Result'
FROM #ProjectMeter pMeter
INNER JOIN #ProjectMaster pMaster ON pMaster.ID = pMeter.ProjectID
GROUP BY pMaster.ID
I have used table variables here but surely you just need to drop the #'s as I have used the same table names as you have specified? Not sure if this is okay? :)
Also in MS SQL you can do it using recursive query with CTE.
Here is a SQLFiddle demo
;with t1 as (
select t.*,
cast(meternumber as varchar(max)) as m2,
0 as level
from ProjectMeter t
where not exists
(select id
from ProjectMeter l
where l.id<t.id and l.ProjectId=t.ProjectID
)
union all
select b.*,
cast(c.m2+','+b.MeterNumber as varchar(max)) as m2,
c.level+1 as level
from ProjectMeter b
inner join t1 c
on (c.id < b.id) and (b.ProjectID=c.ProjectId)
)
select pm.ProjectName as ProjectName,
t1.m2 as MeterNumbers
from t1
inner join
(select ProjectId,max(level) ml
from t1
group by ProjectId
) t2
on (t1.ProjectId=t2.ProjectID) and (t1.level=t2.ml)
left join ProjectMaster pm
on (t1.ProjectId=pm.Id)
order by t1.ProjectID