Issue regarding SQL Server 2005 CTE & FOR XML PATH('') usage - sql-server-2005

I have generated the month number through a CTE and try to generate output like
1,2,3,....30,31 through using for xml but I'm getting an error
Here is my full script
;With CTEMonth As
(Select 1 As Number
Union All
Select m.Number + 1 As Number
From CTEMonth m
Where m.Number <=30)
DECLARE #cols AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + m.Number
from CTEMonth m
order by m.Number
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
print #cols
What is wrong in my SQL? Please guide. thanks

You need to move the DECLARE to before the CTE.
DECLARE #cols AS NVARCHAR(MAX);
;With CTEMonth As
(Select 1 As Number
Union All
Select m.Number + 1 As Number
From CTEMonth m
Where m.Number <=30)
select #cols = STUFF((SELECT ',' + m.Number
from CTEMonth m
order by m.Number
FOR XML PATH(''), TYPE
).value(N'./text()[1]', 'NVARCHAR(MAX)')
,1,1,'');
print #cols;
Also, a recursive CTE is not the most efficient way to generate a set - see here, here and here for better alternatives, such as a numbers table or a calendar table.

Related

How to use CTE with dynamic query pivot?

I am using SQL Server 2008 R2
First I needed to break the dates (start date and end date) into every day from table1 which I did with recursive CTE.
DECLARE #maxdate DATETIME = (SELECT Max([EndDate]) FROM table1);
WITH CTE_DateToDays
AS (SELECT StartDate as Dates
FROM table1
UNION ALL
SELECT Dateadd(day, 1, Dates)
FROM CTE_DateToDays
WHERE Dates < #maxdate)
Select Dates from CTE_DateToDays;
Then I wanted to convert rows into columns from table2. Which I did with dynamic pivot, as I did not know the number of columns.
DECLARE #TimeBandColms AS NVARCHAR(MAX);
Select #TimeBandColms = STUFF((SELECT ',' + QUOTENAME(t.TimeBandName)
from table2 t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
Select #TimeBandColms
from
(
SELECT t.TimeBandID,t.TimeBandName
FROM
table2 t
) x
pivot
(
MAX(TimeBandID)
for TimeBandName in (" + #TimeBandColms + N")
) piv;
Now here I am stuck whenever I combine these two things, sometimes the result set is not what I want or sometimes error with 'Invalid column name' or 'Multi-part identifier could not be bound'.
NOTE
Table1 and Table2 are different tables with nothing in common like key or id.
I have tried many things like union or different approach, but couldn't solve my problem.
Sorry if I am not more specific, but I am new to this pivot, stuff, and CTE.
I really need your help.
The result set which I want is something like this...
Here is my attempt.
DECLARE #TimeBandColms AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #TimeBandColms = STUFF((SELECT ',' + QUOTENAME(t.TimeBandName)
from table2 t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
set #query = 'DECLARE #maxdate DATETIME = (SELECT Max([EndDate]) FROM
table1);
WITH CTE_DateToDays
AS (SELECT StartDate as Dates
FROM table1
UNION ALL
SELECT Dateadd(day, 1, Dates)
FROM CTE_DateToDays
WHERE Dates < #maxdate)'
Set #query += N'SELECT ' + #TimeBandColms + N' from
(
SELECT CTE_DateToDays.Dates, t.TimeBandID,t.TimeBandName
FROM CTE_DateToDays,
table2 t
) x
pivot
(
max(Dates)
for TimeBandName in (' + #TimeBandColms + N')
) piv;'
exec (#query);
You can try this.
DECLARE #TimeBandColms AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #TimeBandColms = STUFF((SELECT ',' + QUOTENAME(t.TimeBandName)
from table2 t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
set #query = 'DECLARE #maxdate DATETIME = (SELECT Max([EndDate]) FROM
table1);
WITH CTE_DateToDays
AS (SELECT StartDate as Dates
FROM table1
UNION ALL
SELECT Dateadd(day, 1, Dates)
FROM CTE_DateToDays
WHERE Dates < #maxdate)'
Set #query += N'SELECT Dates, ' + #TimeBandColms + N' from
(
SELECT CTE_DateToDays.Dates, t.TimeBandID, t.TimeBandName
FROM CTE_DateToDays
CROSS JOIN table2 t
) x
pivot
(
count(TimeBandID)
for TimeBandName in (' + #TimeBandColms + N')
) piv;'
exec (#query);

pivot table sort datetime column

I have the following part of sql statement to pivot dynamic columns. The number of columns (ClosingDate) is variable :
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(ClosingDate + '_'+ c.col)
from #TmpT
cross apply
(
select 'Cnt' col
union all
select 'TT'
) c
FOR XML PATH('') , TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
The problem is that ClosingDate columns get converted to varchar and they are ordered as varchar, not as date .
Is there a way to order by datetime ? Thanks!
You could try converting them to dates yourself first:
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(CONVERT(VARCHAR(8),ClosingDate,112) + '_'+ c.col)
from #TmpT
cross apply
(
select 'Cnt' col
union all
select 'TT'
) c
FOR XML PATH('') , TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')

The column '638' was specified multiple times for 'PVT'. Pivot

I've the following SQL Query
Select Product_Id, [riy] AS [riy],
[eas] AS [eas]
FROM
(SELECT Product_Id, Store_Name, Quantity
FROM [Product_Stock] INNER JOIN Store on Store.Id = [Product_Stock].Stock_Id where Product_Id = 435) ps
PIVOT
(
SUM(Quantity)
FOR Store_Name IN
([riy],[EAST WAREHOUSE - eas])
) AS pvt
it gives the expected result.Giving me total quantity for locations riy and eas.
However, I want to dynamically get the Store names instead of manually specifying them.
this is what I've done.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME([Product_Id])
FROM [Product_Stock]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #query =
'SELECT Product_Id FROM
(SELECT Product_Id, Store_Name, Quantity
FROM [Product_Stock] INNER JOIN Store on Store.Id = [Product_Stock].Stock_Id where Product_Id = 435) PS
PIVOT
(
SUM(Quantity)
FOR Store_Name in (' + #cols + ')
) AS PVT'
EXEC SP_EXECUTESQL #query
This gives me an error saying The column '638' was specified multiple times for 'PVT'.
How can i solve this problem?
Without knowing your actual data this is a blind flight, but I think you have two issues:
You must use DISTINCT to get each value only once
You are not concatenating the stores names but the IDs of your products
Try this
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME([Store_Name])
FROM [Product_Stock]
INNER JOIN Store on Store.Id = [Product_Stock].Stock_Id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Just make sure the cols are DISTINCT
... STUFF((SELECT DISTINCT ','...
Thanks #Shnugo for pushing me in the right direction. Here's the query I finally ended up using.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME([Store].Store_Name)
FROM [Store] INNER JOIN Product_Stock ON Product_Stock.Stock_Id = Store.Id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #query =
'SELECT Product_Id , ' + #cols + ' from
(SELECT Product_Id, Store_Name, Quantity
FROM [Product_Stock] INNER JOIN Store on Store.Id = [Product_Stock].Stock_Id WHERE Product_Stock.product_id = 435 ) PS
PIVOT
(
SUM(Quantity)
FOR Store_Name in (' + #cols + ')
) AS PVT'
EXEC SP_EXECUTESQL #query

MS SQL:Pivot Table order columns

I'm hoping some can help clarify what I should be doing. I'm essentially reading in a Table with dates and then Im trying to convert these dates into individual columns.
Unfortunately the columns come out out of order. How do I order these columns into |01/01/2013|02/01/2013| etc? Any guidance would be much appreciated.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.GL_Effective_Date)
FROM [03_rdm].[Table_2013] c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Ref_Account_Class, ' + #cols + ' from
(
select TOP 100 PERCENT
Ref_Account_Class
, GL_Amount
, GL_Effective_Date
from [03_rdm].[Table_2013]
where Ref_Account_Class = ''Accounts Receivable''
order by GL_Effective_Date
) x
pivot
(
max(GL_Amount)
for GL_Effective_Date in (' + #cols + ')
) p '
execute(#query)
You need to order in this statement:
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.GL_Effective_Date)
FROM [03_rdm].[Table_2013] c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
When using distinct though, you can only order by items in the select list, so you can't order by c.GL_Effective_Date, it would need to be the full statement (',' + QUOTENAME(c.GL_Effective_Date), but since QUOTENAME implicitly converts your date to a VARCHAR these will not come out in order. So instead of DISTINCT you can use GROUP BY to remove duplicates:
SET #cols = STUFF((SELECT ',' + QUOTENAME(c.GL_Effective_Date)
FROM [03_rdm].[Table_2013] c
GROUP BY c.GL_Effective_Date
ORDER BY c.GL_Effective_Date
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Which should give the ordering you require. This should also be marginally more efficient, although probably not noticably. When you use a scalar function with distinct the function is applied to all rows, then duplicates removed from the results, however with group by, first duplicates are removed, then the function is applied to the results. As mentioned, this will not be noticable with QUOTENAME, but be a useful bit of knowledge to have for future reference.
Instead of using distinct, use group by:
SET #cols = STUFF((SELECT ',' + QUOTENAME(c.GL_Effective_Date)
FROM [03_rdm].[Table_2013] c
GROUP BY c.GL_Effective_Date
ORDER BY c.GL_Effective_Date
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),
1, 1, '');

while converting column to row not able to fetch value from another table automatically

select *
from (
select vtid, convert(date, dtime) as Date from Transaction_tbl where locid = 5
) as vt
pivot (
count(vtid)
for vtid in (select vtid from VType_tbl)
) as pvt
while executing this query am getting error
Incorrect syntax near the keyword 'select'." and Incorrect syntax near
')'.
actually I have one more table,name= Vtype_table , How Can I load all vtid from vtype table in this query? I want to get output depend upon vtid.
Any help greatly appreciated.
Your PIVOT syntax is correct except you are using a SELECT statement inside your PIVOT.
You cannot use a SELECT statement inside the PIVOT IN clause to select column headers. It is required that the columns for the IN clause be known prior to executing the query.
If you are looking to generate a dynamic list of vtid values, then you will need to use dynamic SQL to get the result and the syntax will be similar to the following:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(vtid)
from VType_tbl
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Date, ' + #cols + '
from
(
select vtid, convert(date, dtime) as Date
from Transaction_tbl
where locid = 5
) d
pivot
(
count(vtid)
for vtid in (' + #cols + ')
) p '
execute(#query);
Edit, if you want the type names to appear then you should be able to use the following:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(vt_name)
from VType_tbl
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Date, ' + #cols + '
from
(
select v.vt_name, convert(date, dtime) as Date
from Transaction_tbl t
inner join VType_tbl v
on t.vtid = v.vtid
where locid = 5
) d
pivot
(
count(vt_name)
for vt_name in (' + #cols + ')
) p '
execute(#query)
Note: I am guessing on the column name for VType_tbl