Strip the last dynamic column from ending comma? - sql

I am writing the following dynamic SQL, and getting an error around the FROM keyword.
Incorrect syntax near the keyword 'FROM'.
' + #columnList + '
FROM [History]
I know why, its because there shouldn't be a comma that precedes it. However, since the column before it (#columnList) is a result of dynamic SQL, how do I go about resolving this?
Basically, I need a way to make
SELECT #columnList =....
not append a comma at the end to the LAST column/Account selected.
The comma is added at the end at this part:
quotename(AccTbl.Account), ',',
Full Query:
DECLARE #sqlCommand NVARCHAR(MAX) = '',
#columnList NVARCHAR(MAX) = '',
#pivotColumns NVARCHAR(MAX) = '';
SELECT #columnList =
(SELECT DISTINCT concat(CHAR(9), 'COALESCE(', quotename(AccTbl.Account), ', 0)', quotename(AccTbl.Account), ',', CHAR(10)) --CHAR(9) & CHAR(10) for indentation/formatting
FROM [Accounts] AccTbl
WHERE AccTbl.Account NOT IN ('WS')
FOR XML Path(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)');
SELECT #pivotColumns = STUFF(
(SELECT DISTINCT concat(CHAR(9), ',', quotename(AccTbl.Account), CHAR(10))
FROM [Accounts] AccTbl
WHERE AccTbl.Account NOT IN ('WS')
FOR XML Path(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 1, '');
/*
EXEC the sqlCommand as separate batches to prevent this error: 'CREATE VIEW' must be the first statement in a query batch.
https://stackoverflow.com/a/39135516/8397835
*/
SET #sqlCommand = '
USE [ABC_DB]
--GO
DROP VIEW IF EXISTS [dbo].[Piv];
--GO
SET ANSI_NULLS ON
--GO
SET QUOTED_IDENTIFIER ON
--GO
';
Execute sp_executesql #sqlCommand;
SET #sqlCommand = '
CREATE VIEW [dbo].[Piv]
AS
(SELECT
[Style Code],
' + #columnList + '
FROM [History]
PIVOT (SUM([Value]) FOR [Accounts] IN (
' + #pivotColumns + '
)
)
AS Piv);
';
PRINT #sqlCommand;
Execute sp_executesql #sqlCommand;
In other words, whats happening right now is something like this:
UPDATE:
#columnList was fixed with leading comma instead of trailing comma, but leading comma nor trailing comma would work for #pivotColumns because we don't have a pre-existing column in the PIVOT part of the query like we do in the SELECT statement with Style Code.

Don't put the commas at the end, put them at the start, and then strip the first character, using STUFF, that's far easier in T-SQL.
So instead of {Expression} + ',' do ',' + {Expression}. Then you can simply do STUFF(#columnList,1,1,'') to remove the leading comma instead.

Part of the problem is your 'formatting' of the code. Generally I would agree that formatting the dynamic code can help in debugging - here it is getting in the way.
SELECT #columnList = STUFF(
(SELECT DISTINCT concat(', ', quotename(AccTbl.Account))
FROM [Accounts] AccTbl
WHERE AccTbl.Account NOT IN ('WS')
FOR XML Path(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 1, '');
SELECT #pivotColumns = STUFF(
(SELECT DISTINCT concat(', ', quotename(AccTbl.Account))
FROM [Accounts] AccTbl
WHERE AccTbl.Account NOT IN ('WS')
FOR XML Path(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 1, '');
Removing those - we see that both statements are exactly the same. Assume the results from the Accounts table are: Acct1, Acct2
You would get the result as '[Acct1], [Acct2]' for both #columnList and #pivotColumns. So - if you want to expand on the column list portion, for example add the table alias (which is what I would do):
SELECT #columnList = STUFF(
(SELECT DISTINCT concat(', ', 'h.', quotename(AccTbl.Account))
FROM [Accounts] AccTbl
WHERE AccTbl.Account NOT IN ('WS')
FOR XML Path(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 1, '');
I would not bother with formatting these columns in the final result, since you will be recreating the view as needed using dynamic SQL.
SET #sqlCommand = '
CREATE VIEW [dbo].[Piv]
AS
(SELECT
h.[Style Code],
' + #columnList + '
FROM [History] h
PIVOT (SUM(h.[Value]) FOR [Accounts] IN (
' + #pivotColumns + '
)
)
AS Piv);
';

Related

How to convert XML type return text into select columns

I'm trying to get the column names of a table using XML datatype and information_schema columns. When I tried to use the result in another select statement, I have the results with the repeated column name instead of the results set. I have even tried to cast it to varchar but it still failed. what have done wrong ?
DECLARE #TSQL1 varchar(1000);
SELECT #TSQL1 = CAST((SELECT SUBSTRING((SELECT ', ' + QUOTENAME(COLUMN_NAME)
FROM [ProdLS].[ information_schema.columns]
WHERE table_name = 'roles'
ORDER BY ORDINAL_POSITION
FOR XML PATH('')), 3, 200000)) AS varchar(max));
SELECT #TSQL1
FROM [aubeakon_scrm4].[acl_roles]
My query to get the results from roles table using the column name retrieved from.
You cannot execute dynamic SQL like that. You need to use sp_executesql. You also need to declare dynamic SQL as nvarchar(max).
You should also use .value to unescape the XML
DECLARE #TSQL1 nvarchar(max) = N'
SELECT
' + STUFF((
SELECT ', ' + QUOTENAME(COLUMN_NAME)
FROM [ProdLS].[information_schema].columns
WHERE table_name = 'roles'
ORDER BY ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('text()[1]', 'nvarchar(max)'), 1, LEN(', '), '') + '
FROM [aubeakon_scrm4].[acl_roles];
';
EXEC sp_executesql #TSQL1;

how to create dynamic table

I have a dynamic pivoted query which generates a result set and I want to insert that data into a table. But the problem is columns are dropped or generated by the time. So by the time I cannot predict columns. That is why I created a dynamic pivoted dataset. So how to insert that data set into table?
One solution is to drop and recreate the table every time but I don't know how to do it. I tried CTE, TEMP table but EXEC support only select, insert, update, delete statement:
DECLARE #columns NVARCHAR(MAX), #sqlquery NVARCHAR(MAX), #orderby Nvarchar(MAX),#value Nvarchar(max);
SET #columns = N'';
SET #value=N'0'
SELECT #columns += N', ' + QUOTENAME([Note_Type])
FROM
(
SELECT distinct
No_T
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)as A order by No_T
SET #sqlquery = N'
Select
K._Number
,D.C_Number
,' + STUFF(#columns, 1, 2, '') + '
from
(
select
_Number
,' + STUFF(#columns, 1, 2, '') + '
from
(
select distinct
right(REPLICATE('+#value+',11) +_Number,11) as [_Number]
,No_t
,No_T_Des
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)AS J
pivot
(
count(No_T_Des) FOR [No_t] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
)P
)K
left join
[DS_DM].[dbo].[D_TABLE] D on k._Number = D._Number
';
EXEC sp_executesql #sqlquery
I've modified your code to reflect my proposed solution.
IF OBJECT_ID (N'NEW_TABLE', N'U') IS NOT NULL
BEGIN
DROP TABLE NEW_TABLE
END
DECLARE #columns NVARCHAR(MAX), #sqlquery NVARCHAR(MAX), #orderby Nvarchar(MAX),#value Nvarchar(max);
SET #columns = N'';
SET #value=N'0'
SELECT #columns += N', ' + QUOTENAME([Note_Type])
FROM
(
SELECT distinct
No_T
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)as A order by No_T
SET #sqlquery = N'
Select
K._Number
,D.C_Number
,' + STUFF(#columns, 1, 2, '') + '
INTO NEW_TABLE
from
(
select
_Number
,' + STUFF(#columns, 1, 2, '') + '
from
(
select distinct
right(REPLICATE('+#value+',11) +_Number,11) as [_Number]
,No_t
,No_T_Des
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)AS J
pivot
(
count(No_T_Des) FOR [No_t] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
)P
)K
left join
[DS_DM].[dbo].[D_TABLE] D on k._Number = D._Number
';
EXEC sp_executesql #sqlquery
So I found the answers to my questions.
there are 2 fileds which remais static every time. so I've created ETL that drop that table and recreate every single day with those two fileds. and rest of the 66 columns which are dynamic (drops or newly created) every day. so i've made a cursor for that which loop through all of that categories and altering that table that've created and added that code to ETL
So bassically Every single day that ETL Runs Drop the existing table, Create new table with the 2 static filds and altering same table using cursor with the dynamic filds.

Inserting Different Value IN a Table

Well I'm Creating a Table Based on Result Set Of Databases On Server which contain Multiple Databases.
My table will Create a list of column which contain Database_Name
FOR EG:
DECLARE #strt INT,#End INT,#Database NVARCHAR(20), #ColumnDeclaration VARCHAR(2000),#SqlSelect NVARCHAR(MAX),#column_Name NVARCHAR(255)
SELECT * INTO #T FROM SYS.DATABASES
ORDER BY NAME
SELECT #ColumnDeclaration=STUFF(( SELECT ', ' + Name + ' NVARCHAR(255)'
FROM #T
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)'), 1, 1, '')
SET #SqlSelect=' CREATE TABLE Temp_Comp (' + #ColumnDeclaration + ');'
PRINT #SqlSelect
EXEC (#SqlSelect)
SELECT * FROM Temp_Comp
I wan't Insert the Data INTO Temp_Comp Table a specific Value from
the database whose Name will be in Temp_comp Table
Not sure what are you trying to achieve, but I assume that you want something like:
DECLARE #strt INT,#End INT,#Database NVARCHAR(20), #ColumnDeclaration NVARCHAR(MAX),#SqlSelect NVARCHAR(MAX),#column_Name NVARCHAR(255)
SELECT * INTO #T
FROM SYS.DATABASES
ORDER BY NAME;
SELECT #ColumnDeclaration=STUFF(( SELECT ', ' + QUOTENAME(Name) + ' NVARCHAR(255)'
FROM #T
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)'), 1, 1, '');
SET #SqlSelect=' CREATE TABLE Temp_Comp (col_desc NVARCHAR(200), ' + #ColumnDeclaration + ');';
SELECT #SqlSelect;
EXEC (#SqlSelect);
SET #SQLSelect = ' INSERT INTO Temp_Comp(col_desc, ' + REPLACE(#ColumnDeclaration, 'NVARCHAR(255)', '') + ')' +
' SELECT col,'+ REPLACE(#ColumnDeclaration, 'NVARCHAR(255)', '') +' FROM (' +
' SELECT name, sub.* FROM sys.databases' +
' OUTER APPLY (SELECT ''database_id'', CAST(database_id AS NVARCHAR(MAX)) UNION ALL SELECT ''compatibility_level'', CAST(compatibility_level AS NVARCHAR(MAX)) ) sub(col, val)) src' +
' PIVOT (MAX(val) FOR name IN ('+ REPLACE(#ColumnDeclaration, 'NVARCHAR(255)', '') +') ) piv';
SELECT #SQLSelect;
EXEC (#SQLSelect);
DBFiddle
Result:
In order to INSERT you could use dynamic PIVOT. To add more values just extend OUTER APPLY part.
Keep in mind that you've defined columns as ' NVARCHAR(255)' so you may need to convert values before insert.

More than VARCHAR(MAX) values in SQL Server

I have some tables with more than 700 columns/1000 columns.
Now I want to display all columns from this table to ISNULL(col1,0) format because when I use PIVOT/UNPIVOT and if there are some NULL values then they wont be converted to NULL and becomes empty strings. So I am trying to replace those NULLs with 0.
In this example I used sysobjects table so that you can try it in your ssms.
The result of this is incomplete as neither VARCHAR(MAX) nor NVARCHAR(MAX) is enough. How do I get all rows rather than few rows here?
DECLARE #colsUnpivot VARCHAR(MAX)
SET #colsUnPivot = STUFF((
SELECT ',' + 'ISNULL(' + QUOTENAME(name) + ', 0) AS '
+ QUOTENAME(name)
FROM sysobjects t
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
PRINT #colsUnPivot
set #query = 'SELECT id,code_name,lkp_value
FROM
(
SELECT unitid,institution,city,state,zip, '+ #colsUnpivot+'
FROM sysobjects) AS cp
UNPIVOT (lkp_value for code_name IN ('+#colsUnPivot+')
) AS up'
--PRINT #Query
--PRINT #Query1
exec(#query)
I mean the code above does not make sense but I can not produce same thing that I have as i have to use sysobjects here.
But above code is throwing an error:
Msg 102, Level 15, State 1, Line 6
Incorrect syntax near '('.
And that's because there is so much data and it's being truncated.
Here is what my PRINT says,
,ISNULL([opd_
So I still think its truncating.
Your problem is that the PRINT command will truncate the data for display in SSMS.
I would suggest leaving it as XML and doing a SELECT, if you just need to see the data in SSMS without truncating:
DECLARE #colsUnpivot xml
SET #colsUnPivot = (SELECT ',' + 'ISNULL(' + QUOTENAME(name) + ', 0) AS '
+ QUOTENAME(name)
FROM sysobjects t
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)')
SELECT #colsUnPivot
SSMS treats XML output differently and has a higher threshold for truncating the data.
Use SELECT instead of print in your SQL.
SELECT #colsUnPivot
Also, make sure that these values are maxed out in Results to Grid:

Using stuff in dynamic query string sql

I am trying to fetch two columns with comma separated.This code
SELECT STUFF(( SELECT ',' + Cast(Column1 As varchar(50))+'_'+Cast(Column2 As varchar(50)) FROM Table FOR XML PATH(''), TYPE).value('.', NVARCHAR(MAX)), 1, 1, '') As Result
works for me.But i need to use it in a dynamic query string its getting error when i am trying to do this.
Declare #String AS NVARCHAR(MAX)
Set #string='SELECT STUFF(( SELECT ',' + Cast(Column1 As varchar(50))+'_'+Cast(Column2 As varchar(50)) FROM Table FOR XML PATH(''), TYPE).value('.', NVARCHAR(MAX)), 1, 1, '') As Result'
EXEC sp_executesql #String
You need to escape the quotes in dynamic SQL by doubling them up, otherwise SQL Server thinks the quote has ended:
Set #string='SELECT STUFF(( SELECT '','' + Cast(Column1 As varchar(50))+''_''...
http://blog.sqlauthority.com/2008/02/17/sql-server-how-to-escape-single-quotes-fix-error-105-unclosed-quotation-mark-after-the-character-string/