Concatenate Each Row column values into string with comma seperated in SQL - sql

I'm using the AdventureWorks2008R2 sample database.
i need an output like each row should be concatenated into string(column values will be separated by commas)
This is my code:
create proc sales5
as begin
declare #SalesPersonID int
create table #table
(
id int identity(1,1) primary key clustered,
SalesPersonID int
)
create table #table1
(
SalesPersonID int null ,
PostalCode nvarchar (15) null,City varchar (30) null
)
insert into #table
select distinct
SA.BusinessEntityID
from
Sales.SalesPerson SA
join
Sales.SalesOrderHeader S on S.SalesPersonID = SA.BusinessEntityID
and S.SalesPersonID is not null
;WITH CTE
AS (
SELECT DISTINCT s.SalesPersonID
, CAST(A.PostalCode AS NVARCHAR(25)) AS PostalCode
, A.City
, CAST(s.SalesPersonID AS VARCHAR(MAX)) AS SortOrder
FROM Sales.SalesOrderHeader S(NOLOCK)
, Person.Person P(NOLOCK)
, Person.Address A(NOLOCK)
, #table t
WHERE s.SalesPersonID IS NOT NULL
AND S.SalesPersonID = P.BusinessEntityID
AND S.SalesPersonID = A.AddressID
AND S.SalesPersonID IN ('274')
UNION ALL
SELECT a.SalesOrderID
, a.SalesOrderNumber
, NULL
, C.SortOrder + CAST(a.SalesOrderID AS VARCHAR(MAX))
FROM CTE C
INNER JOIN Sales.SalesOrderHeader A(NOLOCK)
ON A.SalesPersonID = C.SalesPersonID
)
SELECT *
FROM cte
union all
select
cast ( P.BusinessEntityID as nvarchar(50)),
cast (P.NationalIDNumber as nvarchar(30)),
cast (P.Gender as nvarchar(20)),
cast (P.BusinessEntityID as VARCHAR(MAX)) + cast ('1' as VARCHAR(MAX))
from HumanResources.Employee P(nolock)
where exists(
select 1 from CTE C
where P.BusinessEntityID=C.SalesPersonID )
ORDER BY SortOrder
set nocount off
end
My output is like :
SalesPersonID PostalCode City SortOrder
274 98027 Issaquah 274
274 502097814 M 2741
43849 SO43849 NULL 27443849
44082 SO44082 NULL 27444082
44508 SO44508 NULL 27444508
44532 SO44532 NULL 27444532
My Expected Output:
274,98027,Issaquah
274,502097814,M
43849,SO43849
44082,SO44082
44508,SO44508
44532,SO44532
Note:no need of sortorder Column ,thats y i necglected in output.
Any one please help on this..
Thanks in advance.

You can concatenate your result using + or CONCAT like this.
SELECT LTRIM(SalesPersonID) + ',' + LTRIM(PostalCode) + ISNULL(',' + LTRIM(City),'')
FROM CTE2
ORDER BY SortOrder
Complete Query
;WITH CTE
AS (
SELECT DISTINCT s.SalesPersonID
, CAST(A.PostalCode AS NVARCHAR(25)) AS PostalCode
, A.City
, CAST(s.SalesPersonID AS VARCHAR(MAX)) AS SortOrder
FROM Sales.SalesOrderHeader S(NOLOCK)
, Person.Person P(NOLOCK)
, Person.Address A(NOLOCK)
, #table t
WHERE s.SalesPersonID IS NOT NULL
AND S.SalesPersonID = P.BusinessEntityID
AND S.SalesPersonID = A.AddressID
AND S.SalesPersonID IN ('274')
UNION ALL
SELECT a.SalesOrderID
, a.SalesOrderNumber
, NULL
, C.SortOrder + CAST(a.SalesOrderID AS VARCHAR(MAX))
FROM CTE C
INNER JOIN Sales.SalesOrderHeader A(NOLOCK)
ON A.SalesPersonID = C.SalesPersonID
), CTE2 as
(
SELECT *
FROM cte
union all
select
cast ( P.BusinessEntityID as nvarchar(50)),
cast (P.NationalIDNumber as nvarchar(30)),
cast (P.Gender as nvarchar(20)),
cast (P.BusinessEntityID as VARCHAR(MAX)) + cast ('1' as VARCHAR(MAX))
from HumanResources.Employee P(nolock)
where exists(
select 1 from CTE C
where P.BusinessEntityID=C.SalesPersonID )
)
SELECT LTRIM(SalesPersonID) + ',' + LTRIM(PostalCode) + ISNULL(',' + LTRIM(City),'')
FROM CTE2
ORDER BY SortOrder

Related

Issue with Syntax Pivoting Rows to Columns

I am trying to pivot rows from one table into columns joined with other tables I am hoping can use in a view.
Here is the Statement:
DECLARE #columns NVARCHAR(MAX) = '';
SELECT #columns+=QUOTENAME(FFName) + ','
FROM TCG_LU_FUND_FAMILY_02
SET #columns = LEFT(#columns, LEN(#columns) - 1);
SELECT *
FROM(select F.PositionID as PID
, P.EmployeeName as EmployeeName
, P.Department as BUName
, P.Office as Office
, NULL as Team
, NULL as Manager
, P.Level as Level
, P.Title as Title
, P.HireDate as HireDate
, F.Headcount as HC
, Q.Average_Cost as Cost
, Q.Haircut as [Haircut %]
, F.Position_Filled_in_Qtr as [Position Filled during Quarter]
, F.Percent_Vacant as "Months in Qtr Position was VACANT 100%,66%,33%"
, F.Percent_Allocated
, FF.FFName as Fund_name
FROM TCG_RegisterFact_02 F
inner join TCG_LU_Position_02 P
on F.PositionID = P.PositionID
inner join TCG_LU_QUARTER_LEVEL_02 Q
on P.Level = Q.Level
inner join TCG_LU_FUND_FAMILY_02 FF
on F.FFName = FF.FFName
) as SrcTbl
PIVOT( sum (Percent_Allocated) FOR Fund_Name
in ('+ #columns +')) as PivTbl;
And here is the error message I receive:
Incorrect syntax near '+ #columns +'. (SQL Error Number -2146232060, 102)

Combine Multi Rows with COALESCE

Using SQL-Server 2012
I have the following Table:
Id Description
6192 Salzburg
6193 Salzburg
6194 Salzburg
6196 Innsbruck
6197 Innsbruck
6198 Innsbruck
6199 Innsbruck
6201 Bregenz
6202 Bregenz
6203 Bregenz
I want to Select each Distinct "Description" with all the Id's together in one string:
Description Ids
Salzburg '6192,6193,6194'
Innsbruck '6196,6197,6198'
I saw some similar code on this site [How to concatenate text from multiple rows into a single text string in SQL server?, but I couldn't figure it out yet for my purpose (don't want to use XML Path!). Here is what I have tried so far:
DECLARE #ids AS Nvarchar(MAX)
SELECT #ids = COALESCE(#ids + ',', '') + CAST(t.Id AS nvarchar(5))
FROM (SELECT tmp.Id FROM (SELECT id, [Description] FROM tblMasterPropValues WHERE IdCategory = 253 AND IsActive = 1) as tmp
WHERE [Description] = tmp.[Description]) AS t
SELECT #ids
--SELECT DISTINCT [Description], #ids AS IDs FROM tblMasterPropValues WHERE IdCategory = 253 AND IsActive = 1 AND Id IN (#ids)
I can't really get my head around it, and would appreciate any help on it.
You can try using STUFF() function
SELECT description, Ids = STUFF(
(SELECT ',' + Id
FROM tblMasterPropValues t1
WHERE t1.description = t2.description
FOR XML PATH (''))
, 1, 1, '') from tblMasterPropValues t2
group by description;
For that FOR XML PATH() is the right clause so, you can do :
SELECT DISTINCT v.description, STUFF(v1.ids, 1, 1, '''') + ''''
FROM tblMasterPropValues v CROSS APPLY
(SELECT ', '+ CAST(v1.Id AS VARCHAR(255))
FROM tblMasterPropValues v1
WHERE v1.description = v.description
FOR XML PATH('')
) v1(ids);
You can also make it by using recursive CTE
DECLARE #tblMasterPropValues TABLE (Id INT, Description VARCHAR(20))
INSERT INTO #tblMasterPropValues VALUES
(6192 , 'Salzburg'),
(6193 , 'Salzburg'),
(6194 , 'Salzburg'),
(6196 , 'Innsbruck'),
(6197 , 'Innsbruck'),
(6198 , 'Innsbruck'),
(6199 , 'Innsbruck'),
(6201 , 'Bregenz'),
(6202 , 'Bregenz'),
(6203 , 'Bregenz')
;WITH Tbl AS
(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY Description ORDER BY Id) AS RN,
COUNT(*) OVER(PARTITION BY Description) AS CNT
FROM #tblMasterPropValues
)
, Rcr AS (
SELECT *, CAST(Id AS varchar(max)) Ids
FROM Tbl WHERE RN = 1
UNION ALL
SELECT T.*, Rcr.Ids + ',' + CAST(T.Id AS VARCHAR(10)) Ids
FROM Rcr
INNER JOIN Tbl T ON T.RN = Rcr.RN + 1 and Rcr.Description = T.Description
)
SELECT RN, Description, Ids FROM Rcr
WHERE RN = CNT
Result:
Description Ids
-------------------- -----------------------
Salzburg 6192,6193,6194
Innsbruck 6196,6197,6198,6199
Bregenz 6201,6202,6203
Try this:
DECLARE #Table TABLE(ID INT, Description VARCHAR(25))
INSERT INTO #Table
VALUES (6192,'Salzburg' )
,(6193,'Salzburg' )
,(6194,'Salzburg' )
,(6196,'Innsbruck')
,(6197,'Innsbruck')
,(6198,'Innsbruck')
,(6199,'Innsbruck')
,(6201,'Bregenz' )
,(6202,'Bregenz' )
,(6203,'Bregenz' )
Query:
SELECT DISTINCT T2.Description,
SUBSTRING(
(
SELECT ','+CAST(T1.ID AS VARCHAR) AS [text()]
FROM #Table T1
WHERE T1.Description = T2.Description
ORDER BY T1.Description
FOR XML PATH ('')
), 2, 1000) [Ids]
FROM #Table T2
Result:
Description Ids
Bregenz 6201,6202,6203
Innsbruck 6196,6197,6198,6199
Salzburg 6192,6193,6194

How to count results of stored procedure and show only 100 rows

I wrote a stored procedure to search the users in my database. But sometimes it returns too many users, and it takes too much time.
So if the result set has over 500 rows, I decided to return only the first 500 rows. But I have no idea how to get count from stored procedure.
Please help me.
My original stored procedure is below. Thanks
ALTER PROCEDURE [dbo].[up_Select_SearchUser]
#companyCode varchar(50)
, #keyword nvarchar(20)
, #isRelative bit
, #langCode varchar(10)
AS
BEGIN
SET NOCOUNT ON;
SELECT
LOWER(M.UserID) AS EmpID,
LOWER(M.EmpID) AS REmpID,
dbo.uf_CompanyPhone(M.CompanyPhone, M.ExtensionNumber) AS ExtensionNumber,
M.CellPhone,
dbo.uf_TeamManagerYN(M.UserID) AS TeamChiefYN,
ISNULL(M.DisplayName, '') AS DisplayName,
ISNULL(M.DisplayName_Eng, '') AS DisplayName_Eng,
M.EMail,
M.MainDeptCode AS DeptCode,
ISNULL(DT.DisplayName, '') AS DeptName,
ISNULL(CL.CompanyCode, '') AS CompanyCode,
ISNULL(CL.CompanyName, '') AS CompanyName,
ISNULL(R.RankCode, '') AS RankCode,
ISNULL(R.RankName, '') AS RankName,
ISNULL(J.JobCode, '') AS JobCode,
ISNULL(J.JobName, '') AS JobName,
ISNULL(D.DutyCode, '') AS DutyCode,
ISNULL(D.DutyName, '') AS DutyName,
ISNULL(M.FaxNumber, '') AS FaxNumber,
ISNULL(M.ADDisplayName, '') AS ADDisplayName,
'' AS Address,
ISNULL(M.CompanyPhone, '') AS CompanyPhone,
ISNULL(R.SortOrder, '') AS RankOrder
FROM
dbo.tb_User M WITH (NOLOCK)
INNER JOIN
dbo.tb_Dept DT WITH (NOLOCK) ON M.MainDeptCode = DT.DeptCode
INNER JOIN
dbo.tb_Rank R WITH (NOLOCK) ON M.RankCode = R.RankCode
AND M.GroupCode = R.GroupCode
AND M.DetailCode = R.DetailCode
AND M.CompanyCode = R.CompanyCode
INNER JOIN
dbo.tb_Company CL WITH (NOLOCK) ON M.CompanyCode = CL.CompanyCode
LEFT OUTER JOIN
dbo.tb_Duty D WITH (NOLOCK) ON M.DutyCode = D.DutyCode
AND M.GroupCode = D.GroupCode
AND M.DetailCode = D.DetailCode
AND M.CompanyCode = D.CompanyCode
LEFT OUTER JOIN
dbo.tb_Job J WITH (NOLOCK) ON M.JobCode = J.JobCode
WHERE
(M.DisplayName LIKE '%' + #keyword + '%'
OR M.UserID LIKE '%' + #keyword + '%')
AND M.DisplayYN = 'Y'
AND M.CompanyCode LIKE #companyCode
ORDER BY
RankOrder, JobCode, DisplayName
END
Add TOP 500 to the select query. This will make sure that the result count will not be more than 500. If it's less than 500 it will take the number of records.If it's greater than 500, it will filter the top 500 records.
ALTER PROCEDURE [dbo].[up_Select_SearchUser]
#companyCode varchar(50)
, #keyword nvarchar(20)
, #isRelative bit
, #langCode varchar(10)
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP 500 LOWER(M.UserID) AS EmpID
, LOWER(M.EmpID) AS REmpID
, dbo.uf_CompanyPhone(M.CompanyPhone, M.ExtensionNumber) AS ExtensionNumber
, M.CellPhone
, dbo.uf_TeamManagerYN(M.UserID) AS TeamChiefYN
, ISNULL(M.DisplayName,'') AS DisplayName
, ISNULL(M.DisplayName_Eng,'') AS DisplayName_Eng
, M.EMail
, M.MainDeptCode AS DeptCode
, ISNULL(DT.DisplayName,'') AS DeptName
, ISNULL(CL.CompanyCode,'') AS CompanyCode
, ISNULL(CL.CompanyName,'') AS CompanyName
, ISNULL(R.RankCode,'') AS RankCode
, ISNULL(R.RankName,'') AS RankName
, ISNULL(J.JobCode,'') AS JobCode
, ISNULL(J.JobName,'') AS JobName
, ISNULL(D.DutyCode,'') AS DutyCode
, ISNULL(D.DutyName,'') AS DutyName
, ISNULL(M.FaxNumber,'') AS FaxNumber
, ISNULL(M.ADDisplayName,'') AS ADDisplayName
, '' AS Address
, ISNULL(M.CompanyPhone, '') AS CompanyPhone
, ISNULL(R.SortOrder,'') AS RankOrder
FROM dbo.tb_User M WITH (NOLOCK)
INNER JOIN dbo.tb_Dept DT WITH (NOLOCK)
ON M.MainDeptCode = DT.DeptCode
INNER JOIN dbo.tb_Rank R WITH (NOLOCK)
ON M.RankCode = R.RankCode
AND M.GroupCode = R.GroupCode
AND M.DetailCode = R.DetailCode
AND M.CompanyCode = R.CompanyCode
INNER JOIN dbo.tb_Company CL WITH (NOLOCK)
ON M.CompanyCode = CL.CompanyCode
LEFT OUTER JOIN dbo.tb_Duty D WITH (NOLOCK)
ON M.DutyCode = D.DutyCode
AND M.GroupCode = D.GroupCode
AND M.DetailCode = D.DetailCode
AND M.CompanyCode = D.CompanyCode
LEFT OUTER JOIN dbo.tb_Job J WITH (NOLOCK)
ON M.JobCode = J.JobCode
WHERE (M.DisplayName LIKE '%' + #keyword + '%'
OR M.UserID LIKE '%' + #keyword + '%')
AND M.DisplayYN = 'Y'
AND M.CompanyCode LIKE #companyCode
ORDER BY RankOrder , JobCode, DisplayName
Use this statement instead of SELECT TOP:
SELECT * FROM Products
WHERE id BETWEEN 10 AND 20;
for a better control
for a better example wiev:
Equivalent of LIMIT and OFFSET for SQL Server?
As i understand from your question is you wants to select only 500 or 100 records its upto u how many records u wants to select simply u can use
select top 500 [lisofyourcolums] from your table;
if u want do a paging u can simply do changes in your Stored procedure like below
ALTER PROCEDURE [dbo].[up_Select_SearchUser]
#companyCode varchar(50)
, #keyword nvarchar(20)
, #isRelative bit
, #langCode varchar(10)
, #startRowIndex int=1
, #maximumRows int=500
AS
BEGIN
SET NOCOUNT ON;
DECLARE #startRow int,#totalRows int
SET #startRowIndex = (#startRowIndex - 1) * #maximumRows
Print #startRowIndex
IF #startRowIndex = 0
SET #startRowIndex = 1
Else
SET #startRowIndex = #startRowIndex+1
Print #startRowIndex
SET ROWCOUNT #startRowIndex
select [yourcolumnList] from yourtable
SET ROWCOUNT #maximumRows
SELECT
END
I hope this will help u...
Select #Count = COUNT()
From Table
Where <Your Condition>
Then use IF statement:
if #Count > 500 then
SELECT TOP 500
LOWER(M.UserID) = AS EmpID
Try this
ALTER PROCEDURE [dbo].[up_Select_SearchUser]
#companyCode varchar(50)
, #keyword nvarchar(20)
, #isRelative bit
, #langCode varchar(10)
, #PageNumber INT = 1--Give Page number you required
, #PageSize INT = 100 --Give number of rows you required per Page
AS
BEGIN
SET NOCOUNT ON;
SELECT LOWER(M.UserID) AS EmpID
, LOWER(M.EmpID) AS REmpID
, dbo.uf_CompanyPhone(M.CompanyPhone, M.ExtensionNumber) AS ExtensionNumber
, M.CellPhone
, dbo.uf_TeamManagerYN(M.UserID) AS TeamChiefYN
, ISNULL(M.DisplayName,'') AS DisplayName
, ISNULL(M.DisplayName_Eng,'') AS DisplayName_Eng
, M.EMail
, M.MainDeptCode AS DeptCode
, ISNULL(DT.DisplayName,'') AS DeptName
, ISNULL(CL.CompanyCode,'') AS CompanyCode
, ISNULL(CL.CompanyName,'') AS CompanyName
, ISNULL(R.RankCode,'') AS RankCode
, ISNULL(R.RankName,'') AS RankName
, ISNULL(J.JobCode,'') AS JobCode
, ISNULL(J.JobName,'') AS JobName
, ISNULL(D.DutyCode,'') AS DutyCode
, ISNULL(D.DutyName,'') AS DutyName
, ISNULL(M.FaxNumber,'') AS FaxNumber
, ISNULL(M.ADDisplayName,'') AS ADDisplayName
, '' AS Address
, ISNULL(M.CompanyPhone, '') AS CompanyPhone
, ISNULL(R.SortOrder,'') AS RankOrder
FROM dbo.tb_User M WITH (NOLOCK)
INNER JOIN dbo.tb_Dept DT WITH (NOLOCK)
ON M.MainDeptCode = DT.DeptCode
INNER JOIN dbo.tb_Rank R WITH (NOLOCK)
ON M.RankCode = R.RankCode
AND M.GroupCode = R.GroupCode
AND M.DetailCode = R.DetailCode
AND M.CompanyCode = R.CompanyCode
INNER JOIN dbo.tb_Company CL WITH (NOLOCK)
ON M.CompanyCode = CL.CompanyCode
LEFT OUTER JOIN dbo.tb_Duty D WITH (NOLOCK)
ON M.DutyCode = D.DutyCode
AND M.GroupCode = D.GroupCode
AND M.DetailCode = D.DetailCode
AND M.CompanyCode = D.CompanyCode
LEFT OUTER JOIN dbo.tb_Job J WITH (NOLOCK)
ON M.JobCode = J.JobCode
WHERE (M.DisplayName LIKE '%' + #keyword + '%'
OR M.UserID LIKE '%' + #keyword + '%')
AND M.DisplayYN = 'Y'
AND M.CompanyCode LIKE #companyCode
ORDER BY RankOrder , JobCode, DisplayName
OFFSET #PageSize * (#PageNumber - 1) ROWS
FETCH NEXT #PageSize ROWS ONLY
END
Declare one variable to get input from user to show how many records he wants to return
ALTER PROCEDURE [dbo].[up_Select_SearchUser]
#companyCode varchar(50)
, #keyword nvarchar(20)
, #isRelative bit
, #langCode varchar(10)
, #NumberOfRows INT
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP #NumberOfRows
LOWER(M.UserID) AS EmpID,
LOWER(M.EmpID) AS REmpID,
dbo.uf_CompanyPhone(M.CompanyPhone, M.ExtensionNumber) AS ExtensionNumber,
M.CellPhone,
dbo.uf_TeamManagerYN(M.UserID) AS TeamChiefYN,
ISNULL(M.DisplayName, '') AS DisplayName,
ISNULL(M.DisplayName_Eng, '') AS DisplayName_Eng,
M.EMail,
M.MainDeptCode AS DeptCode,
ISNULL(DT.DisplayName, '') AS DeptName,
ISNULL(CL.CompanyCode, '') AS CompanyCode,
ISNULL(CL.CompanyName, '') AS CompanyName,
ISNULL(R.RankCode, '') AS RankCode,
ISNULL(R.RankName, '') AS RankName,
ISNULL(J.JobCode, '') AS JobCode,
ISNULL(J.JobName, '') AS JobName,
ISNULL(D.DutyCode, '') AS DutyCode,
ISNULL(D.DutyName, '') AS DutyName,
ISNULL(M.FaxNumber, '') AS FaxNumber,
ISNULL(M.ADDisplayName, '') AS ADDisplayName,
'' AS Address,
ISNULL(M.CompanyPhone, '') AS CompanyPhone,
ISNULL(R.SortOrder, '') AS RankOrder
FROM
dbo.tb_User M WITH (NOLOCK)
INNER JOIN
dbo.tb_Dept DT WITH (NOLOCK) ON M.MainDeptCode = DT.DeptCode
INNER JOIN
dbo.tb_Rank R WITH (NOLOCK) ON M.RankCode = R.RankCode
AND M.GroupCode = R.GroupCode
AND M.DetailCode = R.DetailCode
AND M.CompanyCode = R.CompanyCode
INNER JOIN
dbo.tb_Company CL WITH (NOLOCK) ON M.CompanyCode = CL.CompanyCode
LEFT OUTER JOIN
dbo.tb_Duty D WITH (NOLOCK) ON M.DutyCode = D.DutyCode
AND M.GroupCode = D.GroupCode
AND M.DetailCode = D.DetailCode
AND M.CompanyCode = D.CompanyCode
LEFT OUTER JOIN
dbo.tb_Job J WITH (NOLOCK) ON M.JobCode = J.JobCode
WHERE
(M.DisplayName LIKE '%' + #keyword + '%'
OR M.UserID LIKE '%' + #keyword + '%')
AND M.DisplayYN = 'Y'
AND M.CompanyCode LIKE #companyCode
ORDER BY
RankOrder, JobCode, DisplayName
END

Total sum wrong value in Dynamic Pivot

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

How to declare the columns dynamically in a Select query using PIVOT

I am writing a query to get the address for PersonID. Following query is working for me but it only returns with two Addresses. I want to handle the 'n' number of address with a single query. Is there any way to do this?
Many thanks
SELECT
PersonID, PersonName
[Address1], [Address2]
FROM
(
SELECT
P.PersonID,
P.PersonName,
(ROW_NUMBER() OVER(PARTITION BY P.PersonID ORDER BY A.AddressID)) RowID
FROM tblPerson
INNER JOIN tblAddress AS A ON A.PersonID = P.PersonID
) AS AddressTable
PIVOT
(
MAX(AddressID)
FOR RowID IN ([Address1], [Address2])
) AS PivotTable;
Assuming the following tables and sample data:
USE tempdb;
GO
CREATE TABLE dbo.tblPerson(PersonID INT, PersonName VARCHAR(255));
INSERT dbo.tblPerson SELECT 1, 'Bob'
UNION ALL SELECT 2, 'Charlie'
UNION ALL SELECT 3, 'Frank'
UNION ALL SELECT 4, 'Amore';
CREATE TABLE dbo.tblAddress(AddressID INT, PersonID INT, [Address] VARCHAR(255));
INSERT dbo.tblAddress SELECT 1,1,'255 1st Street'
UNION ALL SELECT 2,2,'99 Elm Street'
UNION ALL SELECT 3,2,'67 Poplar Street'
UNION ALL SELECT 4,2,'222 Oak Ave.'
UNION ALL SELECT 5,1,'36 Main Street, Suite 22'
UNION ALL SELECT 6,4,'77 Sicamore Ct.';
The following query gets the results you want, and shows how it handles 0, 1 or n addresses. In this case the highest number is 3 but you can play with more addresses if you like by adjusting the sample data slightly.
DECLARE #col NVARCHAR(MAX) = N'',
#sel NVARCHAR(MAX) = N'',
#from NVARCHAR(MAX) = N'',
#query NVARCHAR(MAX) = N'';
;WITH m(c) AS
(
SELECT TOP 1 c = COUNT(*)
FROM dbo.tblAddress
GROUP BY PersonID
ORDER BY c DESC
)
SELECT #col = #col + ',[Address' + RTRIM(n.n) + ']',
#sel = #sel + ',' + CHAR(13) + CHAR(10) + '[Address' + RTRIM(n.n) + '] = x'
+ RTRIM(n.n) + '.Address',
#from = #from + CHAR(13) + CHAR(10) + ' LEFT OUTER JOIN xMaster AS x'
+ RTRIM(n.n) + ' ON x' + RTRIM(n.n) + '.PersonID = p.PersonID AND x'
+ RTRIM(n.n) + '.rn = ' + RTRIM(n.n)
FROM m CROSS JOIN (SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_columns) AS n WHERE n.n <= m.c;
SET #query = N';WITH xMaster AS
(
SELECT PersonID, Address,
rn = ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY Address)
FROM dbo.tblAddress
)
SELECT PersonID, PersonName' + #col
+ ' FROM
(
SELECT p.PersonID, p.PersonName, ' + STUFF(#sel, 1, 1, '')
+ CHAR(13) + CHAR(10) + ' FROM dbo.tblPerson AS p ' + #from + '
) AS Addresses;';
PRINT #query;
--EXEC sp_executesql #query;
If you print the SQL you will see this result:
;WITH xMaster AS
(
SELECT PersonID, Address,
rn = ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY Address)
FROM dbo.tblAddress
)
SELECT PersonID, PersonName,[Address1],[Address2],[Address3] FROM
(
SELECT p.PersonID, p.PersonName,
[Address1] = x1.Address,
[Address2] = x2.Address,
[Address3] = x3.Address
FROM dbo.tblPerson AS p
LEFT OUTER JOIN xMaster AS x1 ON x1.PersonID = p.PersonID AND x1.rn = 1
LEFT OUTER JOIN xMaster AS x2 ON x2.PersonID = p.PersonID AND x2.rn = 2
LEFT OUTER JOIN xMaster AS x3 ON x3.PersonID = p.PersonID AND x3.rn = 3
) AS Addresses;
If you execute it, you will see this:
I know the query to get here is an ugly mess, but your requirement dictates it. It would be easier to return a comma-separated list as I suggested in my comment, or to have the presentation tier deal with the pivoting.