I am trying to write a query in CSV format, it is below:
SELECT SUBSTRING
(
(
SELECT ',' + [symbol], + ',' + [date] + ',' + [price]
FROM csvFormatTable
FOR XML PATH('')
)
,2,200000
)
AS CSV
and it mostly gives the correct result apart from the ',' at the end of the line but I do want to replace it with a line break. I've seen CHAR(10) and similar stuff like that but it does not give a line break but instead a '#x0D'. The result of the query above is what I have (without the char(10) stuff) below:
symbol,date,price,mikeCode,2019-04-10,50,mikeCode,2019-04-11,200,mikeCode,2019-04-12,10,
Where as it should be:
symbol,date,price
mikeCode,2019-04-10,50
mikeCode,2019-04-11,200
mikeCode,2019-04-12,10
It needs the line break so it can be readable as a CSV.
Assuming you're on SQL Server 2016- then use (N)CHAR(13) and (N)CHAR(10). You'll then need to use TYPE, to avoid the escaping of the characters, and then value to get the values out.
SELECT STUFF((SELECT ',' + CHAR(13) + CHAR(10) + [symbol] +
',' + [date] + ',' + [price]
FROM csvFormatTable
FOR XML PATH(''),TYPE).value('.','varchar(MAX)'),1,3,'') AS CSV;
If you are on SQL Server 2017+, you can use STRING_AGG
SELECT STRING_AGG(CHAR(13) + CHAR(10) + ',' + [symbol] + ',' + [date] + ',' + [price],CHAR(13) + CHAR(10))
FROM csvFormatTable --Untested
db<>fiddle
Thanks to #Larnu I changed the settings in my SMSS (link on how to do this is here):
How to correctly insert newline in nvarchar
And I just changed my query around a bit to get the wanted results to
DECLARE #SQL NVARCHAR(MAX)
SET #SQL =
'
DECLARE #test NVARCHAR(MAX)
SET #test = (SELECT STRING_AGG(CHAR(10) + [symbol] + '','' + [date] + '','' + [price], CHAR(13) ) FROM csvFormatTable)
SELECT #test
'
EXEC(#SQL)
Related
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);
';
I am using dynamic SQL to concatenate a bunch of scripts that I have loaded into a table. It will take those individual scripts, concatenate them into one script, and then put that finished script into another table. It has worked beautifully so far, concatenating about 17 scripts successfully. But yesterday, I tried 80. And there is the issue. It is capping out at 78,165 characters every time I try it. The guts of it go like this:
SET #FinScriptSQL
= N'SELECT script, row_number( ) over( order by scriptTypeId ) as execOrder INTO #' + #FinScriptName
+ N' from dbo.' + #FinScriptName
+ N';
DECLARE #tsqlStmt NVARCHAR(MAX);
SELECT #tsqlStmt = STUFF(
(
SELECT (CHAR(13) + CHAR(10) + script) AS [text()]
FROM #' + #FinScriptName
+ N'
ORDER BY execOrder
FOR XML PATH(''''), TYPE).value(''.'', ''nvarchar( max )''),
1 ,
1 ,
NULL
);
CREATE TABLE dbo.' + #FinScriptName + N'_FinishedScripts
(
Name sysname NULL,
FinishedScript NVARCHAR(MAX) NULL
);
INSERT INTO dbo.' + #FinScriptName + N'_FinishedScripts
SELECT ''' + #FinScriptName + N''', #tsqlStmt;'
PRINT #FinScriptSQL;
EXECUTE sp_executesql #FinScriptSQL;
The column of the table where the individual scripts are is an NVARCHAR(MAX), as is the destination column of the new table. I thought it might be a setting in SQL Server, so I have already ensured that the maximum characters retrieved is wide open. Any ideas?
I am getting an error described in the title where my code looks like this:
declare
#cols numeric(10,0),
#sql numeric(10,0)
select #cols = isnull(#cols + ', ', '') + '[' + T.AmountPayd + ']' from (select distinct AmountPayd from t1) as T
select #sql = '
select *
from t1 as T
pivot
(
sum(T.AmountPayd) for T.Customer in (' + #cols + ')
) as P'
exec sp_executesql #sql = #sql
The error occurs at this line:
select #cols = isnull(#cols + ', ', '') + '[' + T.AmountPayd + ']' from (select distinct AmountPayd from t1) as T
In my table AmountPayd is declared as numeric data type.
The error I get is:
Msg 8114, Level 16, State 5, Line 108 Error converting data type
varchar to numeric.
You've declared #cols as numeric(10,0), but you're trying to assign text to it.
Probably need to declare it as nvarchar(max).
P.s.
by concatenating AmountPayd you're suppose to get a list of customers?
declare
--#cols numeric(10,0),
--#sql numeric(10,0)
#cols varchar(max),
#sql varchar(max)
--Here you are setting #cols to a concatenated list of the amounts in your table
--The problem is you are trying to concat a decimal or integer into a string without casting it
--This is the same as doing 'A' + 1 and wanting to get A1. You first have to cast it.
--Notice the CAST(T.AmountPayd AS VARCHAR). But cols still needs to be a varchar in your declaration.
select #cols = isnull(#cols + ', ', '') + '[' + CAST(T.AmountPayd AS VARCHAR) + ']' from (select distinct AmountPayd from t1) as T
--Here you are building your dynamic SQL, which is a string which is why #sql must be varchar or nvarchar
select #sql = '
select *
from t1 as T
pivot
(
sum(T.AmountPayd) for T.Customer in (' + #cols + ')
) as P'
exec sp_executesql #sql = #sql
You've almost copied this example line for line, you just missed the declaration of your variables.
http://sqlhints.com/2014/03/18/dynamic-pivot-in-sql-server/
How can I insert an single quotes in a query ?
Example
select *, 'INSERT INTO San_Endereco (Endereco_Id, Logradouro_Id, Bairro_Id, CEP, Logradouro, Livre) VALUES
(' + CAST(Endereco_Id as varchar) + ','
+ CAST(Logradouro_Id as varchar) + ','
+ CAST(Bairro_Id as varchar) + ','
+ CAST (CEP as varchar) + ','
+ CAST(Logradouro as varchar) + ','
+ CAST(Livre as varchar) + ')' as teste
FROM San_Endereco
Before each CAST I need put the single quote. How can I do that ?
Use two single quotes: ''
select *, 'INSERT INTO San_Endereco (Endereco_Id, Logradouro_Id, Bairro_Id, CEP, Logradouro, Livre) VALUES
(''' + CAST(Endereco_Id as varchar) + ''','''
+ CAST(Logradouro_Id as varchar) + ''','''
+ CAST(Bairro_Id as varchar) + ''','''
+ CAST (CEP as varchar) + ''','''
+ CAST(Logradouro as varchar) + ''','''
+ CAST(Livre as varchar) + ''')''' as teste
FROM San_Endereco
Use double single quotes ''
If a single quote is contained in the actual data to be inserted, the command often becomes corrupted. To solve the problem, simply replace any single quote with two quotes (not the double quote character but two single-quote characters).
declare #var varchar(100)
select #var = 'txt'
select char(39)+#var+char(39) -- with single quote
select *, 'INSERT INTO San_Endereco (Endereco_Id, Logradouro_Id, Bairro_Id, CEP, Logradouro, Livre) VALUES
(''' + CAST(Endereco_Id as varchar) + ''','
+ ....
You have to use ' in string two times:
declare #var varchar(100)
select #var = 'txt'
select ' '+#var+' ' -- without single quote
select ''''+#var+'''' -- with single quote
This is probably a bit of a limited, but valuable scenario. I have a SQL Server 2008 database with a table that has millions of records. There appears to be an intermittent problem with several of the records. I'm trying to repro the problem. In an effort to do this, I finally got the ID of an offending record. I would like to generate an INSERT statement associated with this single record in my PROD database. Then I can easily migrate it into my TESTING database in an effort to repro and resolve the problem.
Basically, I need to generate a single INSERT statement for a single record from a single table where I know the primary key value of the record.
Does anyone have any ideas of how I can accomplish this? Essentially, I want to generate insert statements on a conditional basis.
Thank you!
First try to recreate what you want to insert with a SELECT statement.
After that you can insert into the table with a INSERT INTO like this:
INSERT INTO tablename
SELECT ....
If they are on different servers, you can use INSERT like this:
INSERT INTO tablename VALUES (...)
using the values given by the SELECT in the other server fill the values in the insert.
In your specific case I think you can do this:
CREATE PROCEDURE dbo.GenerateSingleInsert
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#pk_value INT -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cols NVARCHAR(MAX), #vals NVARCHAR(MAX),
#valOut NVARCHAR(MAX), #valSQL NVARCHAR(MAX);
SELECT #cols = N'', #vals = N'';
SELECT #cols = #cols + ',' + QUOTENAME(name),
#vals = #vals + ' + ' + REPLICATE(CHAR(39),3) + ','
+ REPLICATE(CHAR(39),3) + ' + ' + REPLICATE(CHAR(39),2) + '+'
+ 'RTRIM(' + CASE WHEN system_type_id IN (40,41,42,43,58,61) THEN
'CONVERT(CHAR(8), ' + QUOTENAME(name) + ', 112) + '' ''
+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
ELSE 'REPLACE(' + QUOTENAME(name) + ','''''''','''''''''''')' END + ')
+ ' + REPLICATE(CHAR(39),2)
FROM sys.columns WHERE [object_id] = OBJECT_ID(#table)
AND system_type_id <> 189 -- can't insert rowversion
AND is_computed = 0; -- can't insert computed columns
SELECT #cols = STUFF(#cols, 1, 1, ''),
#vals = REPLICATE(CHAR(39), 4) + ' + ' + STUFF(#vals, 1, 13, '')
+ REPLICATE(CHAR(39), 2);
SELECT #valSQL = N'SELECT #valOut = ' + #vals + ' FROM ' + #table + ' WHERE '
+ QUOTENAME(#pk_column) + ' = ''' + RTRIM(#pk_value) + ''';';
EXEC sp_executesql #valSQL, N'#valOut NVARCHAR(MAX) OUTPUT', #valOut OUTPUT;
SELECT SQL = 'INSERT ' + #table + '(' + #cols + ') SELECT ' + #valOut;
END
GO
So let's try it out:
CREATE TABLE dbo.splunge
(
ID INT, dt DATETIME, rv ROWVERSION, t NVARCHAR(MAX)
);
INSERT dbo.splunge(ID, dt, t)
SELECT 1, GETDATE(), 'foo'
UNION ALL SELECT 2, GETDATE(), 'bar'
UNION ALL SELECT 3, GETDATE(), 'O''Brien';
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 1;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '1','20120517 10:07:07:330','foo'
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 2;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '2','20120517 10:07:07:330','bar'
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 3;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '3','20120517 10:07:07:330','O''Brien'
If there is an IDENTITY column you may need to set SET IDENTITY_INSERT ON for the TEST table, and verify that there is no collision. Probably about 500 caveats I should mention, I haven't tested all data types, etc.
However in the more general case there is a lot more to it than this. Vyas K has a pretty robust stored procedure that should demonstrate how complicated it can get:
http://vyaskn.tripod.com/code/generate_inserts_2005.txt
You are probably far better off using a tool like Red-Gate's SQL Data Compare to pick a specific row and generate an insert for you. As I've blogged about, paying for a tool is not just about the money, it's about the hours of troubleshooting and bug-fixing that someone else has already done for you.
Aaron,
I liked your code, it solved a problem for me. I ran into a few issues using it (like you said I would) with nulls and the text type so I made some changes to address those issues.
ALTER PROCEDURE dbo.GenerateSingleInsert
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#pk_value INT -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cols NVARCHAR(MAX), #vals NVARCHAR(MAX),
#valOut NVARCHAR(MAX), #valSQL NVARCHAR(MAX);
SELECT #cols = N'', #vals = N'';
SELECT #cols = #cols + ',' + QUOTENAME(name),
#vals = #vals + ' + '','' + ' + 'ISNULL('+REPLICATE(CHAR(39),4)+'+RTRIM(' +
CASE WHEN system_type_id IN (40,41,42,43,58,61) -- datetime types
THEN
'CONVERT(CHAR(8), ' + QUOTENAME(name) + ', 112) + '' ''+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
WHEN system_type_id IN (35) -- text type NOTE: can overflow
THEN
'REPLACE(CAST(' + QUOTENAME(name) + 'as nvarchar(MAX)),'+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
ELSE
'REPLACE(' + QUOTENAME(name) + ','+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
END
+ ')+' + REPLICATE(CHAR(39),4) + ',''null'') + '
FROM sys.columns WHERE [object_id] = OBJECT_ID(#table)
AND system_type_id <> 189 -- can't insert rowversion
AND is_computed = 0; -- can't insert computed columns
SELECT #cols = STUFF(#cols, 1, 1, ''),
#vals = REPLICATE(CHAR(39),2) + STUFF(#vals, 1, 6, '') + REPLICATE(CHAR(39),2) ;
SELECT #valSQL = N'SELECT #valOut = ' + #vals + ' FROM ' + #table + ' WHERE '
+ QUOTENAME(#pk_column) + ' = ''' + RTRIM(#pk_value) + ''';';
EXEC sp_executesql #valSQL, N'#valOut NVARCHAR(MAX) OUTPUT', #valOut OUTPUT;
SELECT SQL = 'INSERT ' + #table + '(' + #cols + ') SELECT ' + #valOut;
END