Select from #localvariable - sql

I have a table of my server path, and I have a stored procedure where I need to call my server path base on the platform. I don't know why it's not working. Below is what I have so far.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [POLSAPSU].[SelectOrdersPOLDate] (#FROM DATETIME, #TO DATETIME)
AS
DECLARE #serverpath varchar(255)
DECLARE #query varchar(max)
BEGIN
SET #serverpath = (SELECT [path] from [param] where [platform] = 'POL')
SET #query = '
select 'POL' + '0'+ordh_sysrefno as ZINDEX,ordh_conttp AS POL_ORD_PKG_TYPE, ordh_pckgrefno AS POL_PKG_REFNO, '''' AS POL_PKG_PRODN, ''1101'' AS SALES_ORG, ''10'' AS DISTR_CHAN, ''11'' AS DIVISION, '''' AS POL_RTV_L_N, '''' AS CT_VALID_F, '''' AS CT_VALID_T, ordh_docno AS PURCH_NO_C
from ' + #serverpath +'.ord_hdr A''
where cast(ordh_createdate as date) BETWEEN #FROM AND #TO
and Exists(select * from ' + #serverpath +'.ord_dtl B where B.ordd_sysrefno = ordh_sysrefno)
union
select ''POL''+pkgh_production+ pkgh_refno, pkgh_type, '''', pkgh_production, ''1101'', ''10'', ''11'', '',convert(varchar(8),pkgh_effst,112), convert(varchar(8),pkgh_effend,112), pkgh_docno
from ' + #serverpath + '.pckg_hdr where cast(pkgh_createdate as date) BETWEEN #FROM AND #TO'
EXEC (#query)
END
I'm getting this error:
Incorrect syntax near '
where cast(ordh_createdate as date) BETWEEN #FROM AND #TO
and Exists(select * from POLTESTSERVER.POL.sysadm.ord_dtl B where '.
I am also considering this kind of approach
Select * from (select serverpath where platform = pol)
but I need to add the table name after the select statement in from clause and I don't have idea how. I am using SQL Server 2019.
Thank you.

I found multiple issues here.
Date parameters inside the sql query string are not properly escaped.
Use concat() function instead of adding those constants and columns
Columns with empty values are not property escaped.
Try below updated script.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [POLSAPSU].[SelectOrdersPOLDate] (#FROM DATETIME, #TO DATETIME)
AS
DECLARE #serverpath varchar(255)
DECLARE #query varchar(max)
BEGIN
SET #serverpath = (SELECT [path] from [param] where [platform] = 'POL')
SET #query = '
select concat(''POL'',''0'', ordh_sysrefno) as ZINDEX
,ordh_conttp AS POL_ORD_PKG_TYPE, ordh_pckgrefno AS POL_PKG_REFNO
, '''' AS POL_PKG_PRODN, ''1101'' AS SALES_ORG, ''10'' AS DISTR_CHAN, ''11'' AS DIVISION
, '''' AS POL_RTV_L_N, '''' AS CT_VALID_F, '''' AS CT_VALID_T, ordh_docno AS PURCH_NO_C
from ' + #serverpath +'.ord_hdr A
where cast(ordh_createdate as date) BETWEEN '''+ cast(#FROM as varchar(30)) +''' AND '''+ cast(#TO as varchar(30)) +'''
and Exists(select * from ' + #serverpath +'.ord_dtl B where B.ordd_sysrefno = ordh_sysrefno)
union
select concat(''POL'', pkgh_production, pkgh_refno), pkgh_type, '''', pkgh_production, ''1101'', ''10'', ''11'', '''', convert(varchar(8),pkgh_effst,112), convert(varchar(8),pkgh_effend,112), pkgh_docno
from ' + #serverpath +'.pckg_hdr where cast(pkgh_createdate as date) BETWEEN '''+ cast(#FROM as varchar(30)) +''' AND '''+ cast(#TO as varchar(30)) +''''
EXEC (#query)
END

Related

SQL Server Dynamic SQL - Get output from list of tables

I am trying to loop through a temp table variable that contains a list of table names. I want to simply count the rows in each table where a DateTracked column is greater than 30 days. I am having trouble dynamically changing the FROM #tblName variable to store the record count and then insert it into my tracking table. Eventually I will use a cursor to loop through each, but I just want to get this logic down first for a single table. Here is my test code:
DECLARE #tblName as NVARCHAR(MAX)
DECLARE #q as NVARCHAR(MAX)
SET #q = 'Select Count(DateTracked) FROM Audit.' + #tblName + ' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'
--DECLARE #tblNameTable TABLE
--(
-- tableName NVARCHAR(MAX)
--)
--INSERT INTO #tblNameTable VALUES (N'myTestTable')
DECLARE #ExpectedRecordsToMove AS TABLE (col1 int)
INSERT INTO #ExpectedRecordsToMove EXECUTE sp_executesql #q, N'#tblName nvarchar(500)', #tblName = 'myTestTable'
SELECT * FROM #ExpectedRecordsToMove
Found solution.
DECLARE #tblName as NVARCHAR(MAX) = 'tblAutoDispatch_DispatchStatus_Map_Tracking'
DECLARE #q as NVARCHAR(MAX) = 'SELECT Count(DateTracked) FROM Audit.' + #tblName + ' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'
DECLARE #ExpectedRecordsToMove TABLE
(
ExpectedRecordsToMove Int
)
INSERT INTO #ExpectedRecordsToMove
EXECUTE sp_executesql #q
SELECT * FROM #ExpectedRecordsToMove
Note: Answer provided by OP on question.
sp_executesql also allows for output parameters. It is possible to assign variable inside the inner query and return the value.
DECLARE #tblName AS NVARCHAR(MAX) = N'tblAutoDispatch_DispatchStatus_Map_Tracking';
DECLARE #q AS NVARCHAR(MAX)
= N'SELECT #rowcount = COUNT(DateTracked) FROM Audit.' + #tblName
+ N' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))';
DECLARE #rowcount INT;
EXECUTE sp_executesql
#query = #q,
#parameters = N'#rowcount INT OUTPUT',
#rowcount = #rowcount OUTPUT;
SELECT
#rowcount;
You can actually do this without a cursor, by building up the query in one go
You must use QUOTENAME to ensure table names do not cause syntax errors
You cannot parameterize a table name, it must be inserted directly into dynamic SQL
DECLARE #tablenames TABLE (schemaName sysname, tblName sysname);
-- insert schema and table names
DECLARE #sql nvarchar(max) = N'
SELECT
ISNULL(tblName, ''Grand Total'') AS tblName,
SUM(rowcount) AS rowcount
FROM (
' +
(
SELECT STRING_AGG(CAST(
N' SELECT ' + QUOTENAME(tn.schemaName, '''') + N'.' + QUOTENAME(tn.tblName, '''') + N' AS tblName,
Count(DateTracked) AS rowCount
FROM ' + QUOTENAME(tn.schemaName) + N'.' + QUOTENAME(tn.tblName) + N'
WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'
AS nvarchar(max)), N'
UNION ALL
')
FROM #tablenames tn
JOIN sys.tables t ON tn.schemaName = SCHEMA_NAME(t.schema_id) AND tn.tblName = t.name
) + N'
) AS tables
GROUP BY ROLLUP(tblName);
';
PRINT #sql; -- for testing
EXEC (#sql);
If you don't have STRING_AGG on your version of SQL Server, you must use FOR XML instead

Could not able to figure out the error near convert sql

It is giving me an error saying 'Incorrect syntax near the keyword 'convert'.' Could not able to figure out what could be done to remove it.
ALTER PROCEDURE [dbo].[sp_convert_unix_timestamp]
#table_name nvarchar(250), ---Staging Table
#DateTimeString nvarchar(max)
AS
BEGIN
if isnull(#DateTimeString,'') <> '' begin
declare #SQLString Nvarchar(max)
declare #UpdateString Nvarchar(max)
declare #initialepoch Nvarchar(10)
set #initialepoch = '19700101'
SELECT #SQLString = coalesce(#SQLString + ',', '') +
'convert(varchar, dateadd(ss, convert(Int, ' + value + '),' + #initialepoch + '), 120)'
FROM STRING_SPLIT(#DateTimeString, ',')
WHERE RTRIM(value) <> ''
select #UpdateString = 'Update [' + #table_name + '] set ' + #SQLString
exec (#SQLString)
end
END
Firstly, let's fix the SP so it's no open to Injection, and the datatype choice for #table_name:
ALTER PROCEDURE [dbo].[sp_convert_unix_timestamp]
#table_name sysname, ---Staging Table
#DateTimeString nvarchar(max)
AS
IF ISNULL(#DateTimeString, '') <> ''
BEGIN
DECLARE #SQLString nvarchar(MAX);
DECLARE #UpdateString nvarchar(MAX);
DECLARE #initialepoch nvarchar(10);
SET #initialepoch = N'19700101';
SELECT #SQLString = COALESCE(#SQLString + ',', '') + N'convert(varchar(10), dateadd(ss, convert(Int, ' + QUOTENAME(value,N'''') + N'), + #dinitialepoch), 120)'
FROM STRING_SPLIT(#DateTimeString, ',')
WHERE RTRIM(value) <> '';
SELECT #UpdateString = N'Update ' + QUOTENAME(#table_name) + N' set ' + #SQLString;
PRINT #UpdateString;
EXEC sp_executesql #SQLString, N'#dinitialepoch nvarchar(10)', #dinitialepoch = #initialepoch;
END;
If we were to run that SP, EXEC [dbo].[sp_convert_unix_timestamp] N'MyTable', N'20170720,20170721';, you'd get this output (and magcially, you know the reason why it's not working):
Update [MyTable] set convert(varchar, dateadd(ss, convert(Int, '20170720'), + #dinitialepoch), 120),convert(varchar, dateadd(ss, convert(Int, '20170721'), + #dinitialepoch), 120)
There is no column in your SET clause. You simply have UPDATE [Table] SET [Value]; The format of an UPDATE statement is UPDATE [Table] SET [Column] = [Value];. What column(s) that needs to be updated, however, I have no idea...

Comma seperated values to SQL IN STATEMENT

If i have a comma separated list of values:
A1,B2,B3
How do i pass this into a variable and then form it into an SQL IN statement.
DECLARE #DATE AS VARCHAR(50)
SELECT #DATE = CONVERT(VARCHAR(8), Getdate(), 112)
--PRINT #DATE
DECLARE #TIME AS VARCHAR(50)
--PRINT TIME
SELECT #TIME = Replace(CONVERT(VARCHAR(5), Getdate(), 108), ':', '')
DECLARE #ID AS VARCHAR(50)
SELECT #ID = Replace(W0128001, 32322, 32323, 3232323, 2323232, ',', ',')
--PRINT #ID
DECLARE #QUERY NVARCHAR(MAX);
SET #QUERY = 'SELECT * INTO BACKUPTABLE_' + #DATE + #TIME
+ '
FROM TABLE
WHERE ID IN (' + '''' + #ID + ''')'
--EXEC #query
PRINT #QUERY
I have tried to do a replace above but i want it so that an end user can PASTE into the values and my script will take care of the commas and properly form it. It should also strip out the last commas from the end.
My output needs to read:
SELECT * INTO BACKUPTABLE_201606061503
FROM TABLE
WHERE ID IN ('W0128001','32322','32323','3232323','2323232')
For one thing, you don't surround it with single quotes:
SET #QUERY = 'SELECT * INTO BACKUPTABLE_' + #DATE + #TIME + '
FROM TABLE
WHERE ID IN (' + #ID + ')';
There are other ways to pass comma-delimited values to a SQL statement, including using a split() function or XML.
CREATE PROCEDURE [dbo].[CreateBackupTable]
#ID varchar(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #DATE VARCHAR(50)= CONVERT(VARCHAR(8), Getdate(), 112);
DECLARE #TIME VARCHAR(50) = Replace(CONVERT(VARCHAR(5), Getdate(), 108), ':', '')
declare #xml xml,#SQL NVARCHAR(MAX);
set #xml = N'<root><r>' + replace(#ID,',','</r><r>') + '</r></root>'
SET #SQL = N' SELECT * INTO ' + QUOTENAME('BACKUPTABLE_' + #DATE + #TIME)
+ N' from TableName '
+ N' where ID IN (
select r.value(''.'',''varchar(max)'') as item
from #xml.nodes(''//root/r'') as records(r)
)'
exec sp_executesql #sql
, N'#ID varchar(100), #xml XML'
, #ID
, #Xml
END

Invalid variable

This stored procedure works until I add in a variable. The #sWHERE variable will be populated from a a fixed input.
I'm not sure what I need to amend ...I've tried adding & and +
Error: 4145, Level 15, State 1, Procedure spUniqueUPRN, Line 12 An expression of non-boolean type specified in a context where a
condition is expected, near 'Group'.
Stored Procedure:
USE [DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spUniqueUPRN]
#sWHERE varchar(Max)
AS
BEGIN
SET NOCOUNT ON;
SELECT DISTINCT UPRN INTO TBLTEMP FROM TblA
WHERE #sWHERE
Group BY UPRN
UNION ALL
SELECT UPRN FROM TblP
Group BY UPRN
END
VERSION 2 based on help so far (no where near final just a step)
USE [DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spUniqueUPRN]
#sWHERE varchar(Max)
AS
BEGIN
SET NOCOUNT ON;
----------
DROP TABLE TBLTEMP
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = 'UPRN INTO TBLTEMP FROM TblA ' + QUOTENAME(#sWHERE) + '
Group BY UPRN
UNION ALL
SELECT UPRN FROM TblP
Group BY UPRN
';
EXEC sp_executesql #SQL;
----------
END
Verion 3
USE [DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
ALTER PROCEDURE [dbo].[spUniqueUPRN]
#sDateFrom Date,
#sDateTo Date,
#sUPRN varchar(Max),
#sRiskRating varchar(10),
#sSurveyCompany varchar(Max),
#sPostcode varchar(10),
#sStreet varchar(Max),
#sRegion Int
AS
BEGIN
SET NOCOUNT ON;
----------
DROP TABLE TBLTEMP
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = 'SELECT UPRN INTO TBLTEMP FROM TblA WHERE 1 = 1 '
IF QUOTENAME(#sDateFrom) IS NOT NULL AND QUOTENAME(#sDateTo) IS NOT NULL
SELECT #SQL = #SQL + ' or SurveyDate BETWEEN ' + QUOTENAME(#sDateFrom) + ' AND ' + QUOTENAME(#sDateTo) + ''''
IF QUOTENAME(#sRiskRating) IS NOT NULL
SELECT #SQL = #SQL + ' or OverAllRiskCategory = ' + QUOTENAME(#sRiskRating) +''''
IF QUOTENAME(#sUPRN) IS NOT NULL
SELECT #SQL = #SQL + ' or UPRN LIKE ' + QUOTENAME(#sUPRN) + ''''
IF QUOTENAME(#sSurveyCompany) IS NOT NULL
SELECT #SQL = #SQL + ' or SurveyCompany LIKE ''%' + QUOTENAME(#sSurveyCompany) + '%'''
SELECT #SQL = #SQL + ' GROUP BY UPRN '
SELECT #SQL = #SQL + ' UNION ALL '
SELECT UPRN FROM TblProperty
IF QUOTENAME(#sPostcode) IS NOT NULL
SELECT #SQL = #SQL + ' or Postcode LIKE ''%' + QUOTENAME(#sPostcode) + '%'''
IF QUOTENAME(#sStreet) IS NOT NULL
SELECT #SQL = #SQL + ' or Street LIKE ''%' + QUOTENAME(#sStreet) + '%'''
IF QUOTENAME(#sRegion) IS NOT NULL
SELECT #SQL = #SQL + 'or Region LIKE ''%' + QUOTENAME(#sRegion) + '%'''
SELECT #SQL = #SQL + 'GROUP BY UPRN'
EXEC sp_executesql #SQL;
----------
END
First of all to pass #sWHERE as string to stored procedure you need to use dynamic SQL.
Before you use dynamic SQL I strongly recommend to read The Curse and Blessings of Dynamic SQL by Erland Sommarskog.
Your case is SELECT * FROM tbl WHERE #condition:
If you are considering to write the procedure
CREATE PROCEDURE search_sp #condition varchar(8000) AS
SELECT * FROM tbl WHERE #condition
Just forget it. If you are doing this, you have not completed the transition to use stored procedure and you are still assembling your
SQL code in the client. But this example lapses into Dynamic Search Conditions/Dynamic Crosstab.
The point is that you could use (as proposed in comments)
DECLARE #sql NVARCHAR(MAX) =
N'SELECT *
FROM table_name
WHERE ' + #sWHERE;
EXEC dbo.sp_executesql
#sql;
But this is straight way to SQL Injection attacks. User may call:
EXEC dbo.spUniqueUPRN '1=1; DROP TABLE ....; --'
To sum up, it is up to you if you will use WHERE #condition. It is possible but I would not go this path.

How to grab the value of the output parameter in execute sp_executesql?

Please forgive newbie's ignorance!
How do I grab the value of the output parameter in execute sp_executesql?
I can see the output but cannot get to it:
DECLARE #LastActivity nvarchar(100)
DECLARE #LastActivityDate datetime
DECLARE #sql nvarchar(MAX)
DECLARE #RowsToProcess int
DECLARE #CurrentRow int
DECLARE #SelectCol1 nvarchar(100)
DECLARE #SelectCol2 nvarchar(100)
DECLARE #SelectCol3 nvarchar(100)
DECLARE #LastDate TABLE (RowID int not null primary key identity(1,1), col4 nvarchar(MAX), col5 sql_variant)
DECLARE #table1 TABLE (RowID int not null primary key identity(1,1), col1 nvarchar(100),col2 nvarchar(100),col3 nvarchar(100))
INSERT into #table1 (col1,col2,col3)(SELECT t.name AS col1, c.name AS col2, m.Field1 as col3
FROM sys.columns c INNER JOIN
sys.tables t ON c.object_id = t.object_id INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id INNER JOIN
dbo.MERGE_TABLES m ON m.Table_Name=t.name
WHERE c.name LIKE '%[_]DATE%' and m.[Enabled]='Y')
SET #RowsToProcess=##ROWCOUNT
SET #CurrentRow=0
WHILE #CurrentRow<#RowsToProcess
BEGIN
SET #CurrentRow=#CurrentRow+1
SELECT #SelectCol1=col1,#SelectCol2=col2,#SelectCol3=col3 FROM #table1 WHERE RowID=#CurrentRow
SET #sql='SELECT ' + '[dbo].[ConvertToDatetime](MAX(' + #SelectCol2 + '))' + ' FROM ' + #SelectCol1 + ' Where ' + #SelectCol3 + ' = ' + '''0722607QZ'''
Declare #params as nvarchar(MAX)
Set #params = '#date sql_variant output'
Declare #date as sql_variant;
execute sp_executesql
#sql
,#params
,#date output
Select #date
INSERT into #LastDate VALUES (#sql, #date)
end
select col4,col5 from #LastDate
select col4,col5 from #LastDate gives me the SQL script in clo4 but col5 is empty! I need to store the #date as I still need to get the Max(#date)
Thanx a million.
SET #sql='set #date =('SELECT ' + '[dbo].[ConvertToDatetime](MAX(' +
#SelectCol2 + '))' + ' FROM ' + #SelectCol1 + ' Where ' + #SelectCol3
+ ' = ' + '''0722607QZ''' ) '
the above sql gives me error: Incorrect syntax near '.'
SET #sql='set #date =(SELECT [dbo].[ConvertToDatetime](MAX( + #SelectCol2 + ))
FROM #SelectCol1 Where #SelectCol3 ''=0722607QZ'' ) '
The above sql gives the error: Must declare the scalar variable "#SelectCol2"
SET #sql='SELECT ' + #date + '=convert(nvarchar(100), [dbo].[ConvertToDatetime](MAX(' + #SelectCol2 + ')))' + ' FROM ' + #SelectCol1 + ' Where ' + #SelectCol3 + ' = ' + '''0722607QZ'''
the above produces the error : Implicit conversion from data type sql_variant to nvarchar is not
allowed. Use the CONVERT function to run this query.
SET #sql='SELECT ' + #date + '=convert(nvarchar(MAX),(MAX(' + #SelectCol2 + '))' + ' FROM ' + #SelectCol1 + ' Where ' + #SelectCol3 + ' = ' + '''0722607QZ'''
the above produces no error but all output is NULL, no values.
Your syntax looks ok but you never assign to the output variable #date hence no value.
Instead of;
SET #sql='SELECT ...'
You need;
SET #sql='set #date = (SELECT ...'
Can't you use a better type than sql_variant?