have the following table
table TableAB(
ColumnA int,
ColumnB nvarchar(50)
)
i need to create a procedure which returns ColumnA, ColumnB and takes two parameters (in real life count of parameters more then 20). All searches should work with wildcard "%".
procedure has input parameters
#ColumnA int,
#ColumnB nvarchar(50)
i have two approaches
1.
select ColumnA,ColumnB from TableAB
where
ColumnA like
case #ColumnA
when #ColumnA NULL then ColumnA
else '%' + CONVERT(varchar(10),#ColumnA)+'%'
end
and
ColumnB like
case #ColumnB
when #ColumnB NULL then ColumnB
else '%' + ColumnB +'%'
end
2.
DECLARE #TabWhereConditions TABLE(Id INT IDENTITY(1,1), Condition VARCHAR(MAX))
...
SET #ParamDefenition = '#pColumnA int, #pColumnB nvarchar(50)'
IF(NOT #ColumnA IS NULL)
BEGIN
INSERT INTO #TabWhereConditions(Condition)
VALUES('ColumnA like ' + '''%' + CONVERT(varchar(10),#ColumnA) + '%''')
END
IF(NOT #ColumnB IS NULL)
BEGIN
INSERT INTO #TabWhereConditions(Condition)
VALUES('ColumnA like ' + '''%' + #ColumnB + '%''')
END
DECLARE CondCursor CURSOR FOR
SELECT Condition FROM #TabWhereConditions
OPEN CondCursor
SET #WhereString = ''
FETCH NEXT FROM CondCursor INTO #WhereCondition
WHILE ##FETCH_STATUS = 0
BEGIN
SET #WhereString = #WhereString + #WhereCondition + ' AND '
FETCH NEXT FROM CondCursor INTO #WhereCondition
END
CLOSE CondCursor
DEALLOCATE CondCursor
SET #WhereString = SUBSTRING(#WhereString,1, LEN(#WhereString)-4)
SET #SqlCommand = '
SELECT
ColumnA,
ColumnB
FROM TableAB
WHERE ' + #WhereString
EXECUTE sp_executesql #SqlCommand, #ParamDefenition,
#pColumnA = #ColumnA,
#pColumnB = #ColumnB,
Which approach is better ? first or second , or your suggestion
Note: i need solution for situation when procedure can take from 1 up to 20 parameters, each call can get different number of parameters
I believe you want to use something along these lines
select ColumnA,ColumnB from TableAB
where (#columnA is null or ColumnA like '%'+CONVERT(varchar(10),#ColumnA)+'%' and
(#columnB is null or ColumnB like '%'+CONVERT(varchar(10),#ColumnB)+'%'
As noted in this Aaron Bertrand blog post, it is a good idea to make it dynamic T-SQL. Therefore, you may use sys.sp_executesql to run the above statement.
You can create execute stored procedure or query as shown below in case of multiple where condition.
CREATE PROCEDURE [dbo].[SearchUserPermission]
#UserId bigint,
#ParentModuleId Bigint
AS
BEGIN
SET NOCOUNT ON
Declare #strSQLQuery AS VARCHAR(3000) =null;
Declare #strCriteria AS VARCHAR(1000)=null;
BEGIN
SET #strCriteria = ' WHERE 1=1'
IF(#UserId > 0)
BEGIN
SET #strCriteria=#strCriteria + ' AND UserId =' + CONVERT(VARCHAR,#UserId)
END
IF( #ParentModuleId > 0)
BEGIN
SET #strCriteria=#strCriteria + ' AND ParentModuleId=' + Convert(varchar,#ParentModuleId)
END
SET #strSQLQuery ='Select * From (
SELECT
*
FROM Module_Mst
LEFT outer join UserPermissions_Mst
on UserPermissions_Mst.ModuleId=Module_Mst.moduleId AND Module_Mst.IsVisible=1
' +#strCriteria + ' ) AS TempUserPermission'
if (LEN(#strCriteria) > 0 )
BEGIN
SET #strSQLQuery = #strSQLQuery + ' ORDER By OrderNo '
END
END
--Print(#strSQLQuery)
EXEC (#strSQLQuery)
END
Hope this will help you.
Related
I am trying to create optional parameters in a stored process in which I group by the parameters under certain conditions.
For example:
SELECT
TP.ProductID,
case
when #passangers='Y' then (TP.Passangersgroup)
when #fareclass='Y' then (TP.Fareclass)
when #ispriorbooking='Y' then (TP.IsPriorBooking)
end
INTO ##B
FROM ##A TP
GROUP BY
TP.ProductID,
case
when #passangers='Y' then (TP.Passangersgroup)
when #fareclass='Y' then (TP.Fareclass)
when #ispriorbooking='Y' then (TP.IsPriorBooking)
end
In this case, I would be able to select 'Y' for any of the 3 parameters, and I would want to add them to select statement and group by.
Any ideas?
You need to do this with dynamic SQL; something like:
declare #sql varchar(max) = 'SELECT
TP.ProductID, ' +
case when #passangers='Y' then 'TP.Passangersgroup'
when #fareclass='Y' then 'TP.Fareclass'
when #ispriorbooking='Y' then 'TP.IsPriorBooking'
else ''
end
+ ' INTO ##B
FROM ##A TP'
--ETC
Exec(#sql)
If you want to add up to all three columns, you need three case statements:
declare #sql varchar(max) = 'SELECT
TP.ProductID, ' +
case when #passangers='Y' then 'TP.Passangersgroup' else '' end
+ case when #fareclass='Y' then 'TP.Fareclass' else '' end
--ETC.
+ ' INTO ##B
FROM ##A TP'
Dynamic SQL will be the best bet, but I would figure out the column you want and then pass in the one column as a variable. Less likely to suffer SQL injection and more readable.
DECLARE #passangers CHAR(1), #fareclass CHAR(1), #ispriorbooking CHAR(1)
SET #passangers='Y'
DECLARE #SQLCMD NVARCHAR(MAX), #YValue NVARCHAR(1000)
--set the select and group by field
SELECT #YValue=
case
when #passangers='Y' then N'TP.Passangersgroup'
when #fareclass='Y' then N'TP.Fareclass'
when #ispriorbooking='Y' then N'TP.IsPriorBooking'
else NULL
end
IF #YValue IS NOT NULL
BEGIN
SET #SQLCMD=N'
SELECT
TP.ProductID,'+#YValue+'
INTO ##B
FROM ##A TP
GROUP BY
TP.ProductID, '+#YValue
PRINT #SQLCMD
--EXEC sp_executesql #SQLCMD
END
ELSE
PRINT 'INVALID PARAMETER PASSED IN'
You have to use dynamic sql but case statement mentioned in Steve Mangiameli code will not work in case when more than one column is selected as 'Y'. The below code will be working fine for multiple columns selected as 'Y'-
create procedure proc1
#passangers varchar(100) = null,
#fareclass varchar(100) = null,
#ispriorbooking varchar(100) = null
as
begin
declare #sql nvarchar(100)
declare #var varchar(100)
if #passangers = 'y'
set #var = tp.Passangersgroup + ', '
if #fareclass = 'y'
set #var = #var + TP.Fareclass + ', '
if #ispriorbooking = 'y'
set #var = #var + TP.IsPriorBooking
set #sql = 'select ' + #var + ' into ##b from ##a as TP group by ' + #var + 'option(recomplile)'
exec sp_executesql #sql
end
I wrote a stored procedure that gets:
"SearchKeys" - keys to search separated by ',' "key1,key2"
"ToSearch" - Tables to search in separated by ',' with colums after ':' separated by '.' "table1:column1.column2,table2:column1.column2"
At the end procedure returns table with name of table and row id were the key was found.
here is the code:
--Search keys in tables
CREATE PROCEDURE [dbo].[Search_All]
(
#SearchKeys nvarchar(50), --Keys to search separated by ','
#ToSearch varchar(200) --Tables to search in separated by ',' with colums after ':' separated by '.'
)
AS
BEGIN
--create table with found values
CREATE TABLE #Results (TargetId int, DBName varchar(20))
--Split SearchKeys to Keys
WHILE LEN(#SearchKeys) > 0
BEGIN
DECLARE #Key NVARCHAR(25)
IF CHARINDEX(',',#SearchKeys) > 0
SET #Key = SUBSTRING(#SearchKeys,0,CHARINDEX(',',#SearchKeys))
ELSE
BEGIN
SET #Key = #SearchKeys
SET #SearchKeys = ''
END
--Split ToSearch to Tables
WHILE LEN(#ToSearch) > 0
BEGIN
DECLARE #TableAndColums VARCHAR(200)
IF CHARINDEX(',',#ToSearch) > 0
SET #TableAndColums = SUBSTRING(#ToSearch,0,CHARINDEX(',',#ToSearch))
ELSE
BEGIN
SET #TableAndColums = #ToSearch
SET #ToSearch = ''
END
SET #ToSearch = REPLACE(#ToSearch,#TableAndColums + ',' , '')
--Split #TableAndColums to Table and Colums
--Select Table
DECLARE #Table VARCHAR(25)
SET #Table = SUBSTRING(#TableAndColums,0,CHARINDEX(':',#TableAndColums))
SET #TableAndColums = REPLACE(#TableAndColums,#Table + ':' , '')
--Split to Colums
WHILE LEN(#TableAndColums) > 0
BEGIN
DECLARE #Column VARCHAR(25)
IF CHARINDEX('.',#TableAndColums) > 0
SET #Column = SUBSTRING(#TableAndColums,0,CHARINDEX('.',#TableAndColums))
ELSE
BEGIN
SET #Column = #TableAndColums
SET #TableAndColums = ''
END
BEGIN
--insert result in to #Results table
INSERT INTO #Results
EXEC
(
'SELECT ' + #Table + '.Id AS ''TargetId'', '''+#Table+''' AS ''DBName''
FROM ' + #Table +
' WHERE ' + #Column + ' LIKE N''%' + #Key + '%'''
)
END
SET #TableAndColums = REPLACE(#TableAndColums,#Column + '.' , '')
END
END
SET #SearchKeys = REPLACE(#SearchKeys,#Key + ',' , '')
END
--return found values
SELECT DISTINCT TargetId , DBname FROM #Results
END
For some reason it searches only for the first key ignoring all the rest keys. I can not find out why this is happening. Please help!
The very first thing I'll warn you about here is your procedure is wide open to injection attack. Injection attack is in and of itself a broad topic. If you're interested, I suggest reading this article. If you do absolutely need this type of interface (i.e. you can't use static typed SQL or something like Entity Framework to take care of the queries for you), you must must MUST make sure that any strings being executed at run time (e.g. #column, #table, #key) are parametrized or bracketed. This procedure, as written, will also fail when an inputted table does not contain an ID column or when an inputted column doesn't exist.
http://www.sommarskog.se/dynamic_sql.html
In terms of how you're doing string splitting, I'd look at the article below. While there's no way to eliminate the need to loop over each table, by putting all your search strings into a table using a string splinting function like the ones mentioned in this article, you can search all search conditions on a single table at once. Something like this:
select *
from #SearchConditions a
inner join dbo.TargetTable b
on b.Name like '%' + a.SearchKey + '%'
http://www.sqlservercentral.com/articles/Tally+Table/72993/
I am writing a stored procedure in SQL and I am currently in the position of having 3 nested cursors (which isn;t running quite how I would like). I'd appreciate any suggestions on how I can break out of the cursor is the solution trap
I basically have 2 tables (both temporary during the SP populated by SELECT statements from the application)
1 - A list of tables, columns and datatypes
Table1 | SURNAME | VARCHAR
| SEX | VARCHAR
| DOB | DATETIME
------------------------------
Table2 | ADDRESS | VARCHAR
------------------------------
Table3 | SALARY | INT
------------------------------
Table4 | USERNAME | VARCHAR
| PASSWORD | VARCHAR
2 - A one column list of user numbers
My application has to loop through each user number, then for each user I have to loop through the distinct tables (Table1, Table2, Table3, Table4) and see if there is any data in that tabel for that user (using dyanmic SQL so I can pass the table name as a parameter). If there is, I then have to then loop through each column that relates to that table and build a dynamic SQL INSERT statement
So for user number 2...
any data in Table1? No - skip
any data in Table2? Yes - copy rows into temp table then build dynamic SQL for ADDRESS value
any data in Table3? No - skip
any data in Table4? Yes - copy rows into temp table then build dynamic SQL for USERNAME and PASSWORD columns
The cursor approach works fine from a functionality point of view but performance is a little slow. I've made sure the 2 source tables are as tight as possible, when creating my dynamic SQL I copy the relevant rows only into a temp table to process, I've made my cursors FAST_FORWARD and READ_ONLY
Is there any approach I can take instead?
ORIGINAL CODE POSTED AS REQUESTED:
IF OBJECT_ID('tempdb..##MembersToDelete') IS NOT NULL DROP TABLE ##MembersToDelete
IF OBJECT_ID('tempdb..##TempDataTable') IS NOT NULL DROP TABLE ##TempDataTable
DELETE FROM #CompSetTable
DELETE FROM DataRefreshDeletes
DELETE FROM DataRefreshInserts
--BUILD THE INDIVIDUAL SELECT STATEMENTS INTO A TEMP TABLE
SET #RowPosition = 1;
SET #inSelectFilter = #inSelectFilter + ';';
SET #inSelectFilter = REPLACE(#inSelectFilter,'/','''');
WHILE LEN(#inSelectFilter) > 0
BEGIN
IF PATINDEX('%;%',#inSelectFilter) > 0
BEGIN
SET #SelectParameter = SUBSTRING(#inSelectFilter, 0, PATINDEX('%;%',#inSelectFilter))
SET #inSelectFilter = SUBSTRING(#inSelectFilter, LEN(#SelectParameter + ';') + 1,LEN(#inSelectFilter))
IF #RowPosition = 1
BEGIN
INSERT INTO #SelectParameterTable VALUES ('WHERE ' + #SelectParameter)
END
ELSE
BEGIN
INSERT INTO #SelectParameterTable VALUES (' AND ' + #SelectParameter)
END
SET #RowPosition = #RowPosition + 1
END
ELSE
BEGIN
SET #SelectParameter = #inSelectFilter
SET #inSelectFilter = NULL
END
END
--BUILD THE COMPLETE DELETE STATEMENT
SET #SelectParameter = NULL;
SELECT #SelectParameter = COALESCE(#SelectParameter, '') + statementString FROM #SelectParameterTable
--INSERT THE MEMBER NUMBERS INTO THE TEMP TABLE
IF OBJECT_ID('tempdb..##MembersToDelete') IS NOT NULL DROP TABLE ##MembersToDelete
SET #SelectParameter = 'SELECT MEMBNO INTO ##MembersToDelete FROM BASIC ' + #SelectParameter
BEGIN TRY
EXECUTE sp_executesql #SelectParameter
END TRY
BEGIN CATCH
Print 'The following statement could not be run - please check the syntax...'
Print #SelectParameter
GOTO cleanUpAndFinish
END CATCH
SELECT #MembersToDeleteCount = COUNT(*) FROM ##MembersToDelete
Print '##MembersToDelete TABLE BUILT - ' + CONVERT(VARCHAR(26), GETDATE(), 109)
--BUILD LIST OF COMPENDIA TABLES (ORDERED BY DSET)
DELETE FROM #CompSetTable
INSERT INTO #CompSetTable SELECT d.DSNAME, c.column_name, d.DSET, c.data_type FROM DICTIONARY d, INFORMATION_SCHEMA.COLUMNS c WHERE DNUM = -1 AND DSET < 250 AND c.table_name = d.DSNAME ORDER BY d.DSET
Print '#CompSetTable TABLE BUILT - ' + CONVERT(VARCHAR(26), GETDATE(), 109)
DECLARE setInsertCursor CURSOR GLOBAL FAST_FORWARD READ_ONLY FOR SELECT DISTINCT setName, setNumber FROM #CompSetTable ORDER BY setNumber
--WE NOW HAVE THE LIST OF MEMBER NUMBERS AND THE LIST OF TABLES TO BUILD THE DELETE STATEMENT
SELECT #MemberNumberString = COALESCE(#MemberNumberString + ', ', '') + LTRIM(STR(MEMBNO)) FROM ##MembersToDelete
DECLARE setDeleteCursor CURSOR READ_ONLY SCROLL FOR SELECT DISTINCT setName, setNumber FROM #CompSetTable ORDER BY setNumber
OPEN setDeleteCursor
FETCH LAST FROM setDeleteCursor INTO #SetName, #SetNumber
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #DeleteStatementTable VALUES ('DELETE FROM ' + UPPER(#SetName) + ' WHERE MEMBNO IN (' + #MemberNumberString + ')')
FETCH PRIOR FROM setDeleteCursor INTO #SetName, #SetNumber
END
CLOSE setDeleteCursor
DEALLOCATE setDeleteCursor
Print '#DeleteStatementTable TABLE BUILT - ' + CONVERT(VARCHAR(26), GETDATE(), 109)
DECLARE memberInsertCursor CURSOR FOR SELECT MEMBNO FROM ##MembersToDelete
OPEN memberInsertCursor
FETCH NEXT FROM memberInsertCursor INTO #MemberNumber
WHILE ##FETCH_STATUS = 0
BEGIN
--NOW BUILD THE INSERT STATEMENTS
OPEN setInsertCursor
FETCH NEXT FROM setInsertCursor INTO #SetName, #SetNumber
WHILE ##FETCH_STATUS = 0
BEGIN
--CHECK IF MEMBER HAS ANY ROWS IN THIS SET - IF NOT, SKIP
SET #ROWCOUNT = 0
SELECT #COUNTSQL = N'SELECT #countOUT = COUNT(*) FROM ' + #SetName + ' WHERE MEMBNO = ' + LTRIM(STR(#MemberNumber))
EXEC sp_executesql #COUNTSQL, N'#countOUT INT OUTPUT', #countOUT=#ROWCOUNT OUTPUT;
IF #ROWCOUNT = 0
BEGIN
GOTO nextSet
END
SET #VALUES = NULL;
--DROP TEMPORARY TABLE
IF OBJECT_ID('tempdb..##TempDataTable') IS NOT NULL DROP TABLE ##TempDataTable
--POPULATE TEMPORARY TABLE
SET #SQL = 'SELECT * INTO ##TempDataTable FROM ' + #SetName + ' WHERE MEMBNO = ' + LTRIM(STR(#MemberNumber))
EXECUTE sp_executesql #SQL
--BUILD SELECT STATEMENT
SET #INSERTSTRING = NULL
SET #INSERTSTRING = CAST('' as nVarChar(MAX)) + 'SELECT ''INSERT INTO ' + #SetName + ' VALUES ('''
DECLARE setColumnCursor CURSOR FAST_FORWARD READ_ONLY FOR SELECT columnName, dataType FROM #CompSetTable WHERE setName = #SetName
OPEN setColumnCursor
FETCH NEXT FROM setColumnCursor INTO #ColumnName, #DataType
WHILE ##FETCH_STATUS = 0
BEGIN
IF #DataType IN ('text','varchar','nvarchar','ntext','char')
BEGIN
SET #INSERTSTRING = CAST('' as nVarChar(MAX)) + #INSERTSTRING + ''''' + ISNULL(' + #ColumnName + ',''NULL'') + '''''','''
END
ELSE IF #DataType IN ('int','decimal','smallint','numeric','tinyint','bigint','float')
BEGIN
--SET #INSERTSTRING = #INSERTSTRING + ' + ' + #COLUMNNAMENULL + ' + '','''
SET #INSERTSTRING = CAST('' as nVarChar(MAX)) + #INSERTSTRING + ' + ISNULL(CONVERT(VARCHAR(MAX),' + #ColumnName + '),''NULL'')' + ' + '','''
END
ELSE IF #DataType IN ('datetime')
BEGIN
--SET #INSERTSTRING = #INSERTSTRING + ' + ' + #COLUMNNAMENULL + ' + '','''
SET #INSERTSTRING = CAST('' as nVarChar(MAX)) + #INSERTSTRING + ''''' + ISNULL(CONVERT(VARCHAR(MAX),' + #ColumnName + '),''NULL'')' + ' + '''''','''
END
FETCH NEXT FROM setColumnCursor INTO #ColumnName, #DataType
END
CLOSE setColumnCursor
DEALLOCATE setColumnCursor
SET #INSERTSTRING = #INSERTSTRING + '+'')'''
SET #INSERTSTRING = #INSERTSTRING + ' FROM ##TempDataTable'
INSERT INTO #InsertStatementTable EXECUTE sp_executesql #INSERTSTRING
nextSet:
FETCH NEXT FROM setInsertCursor INTO #SetName, #SetNumber
END
FETCH NEXT FROM memberInsertCursor INTO #MemberNumber
END
CLOSE memberInsertCursor
DEALLOCATE memberInsertCursor
CLOSE setInsertCursor
DEALLOCATE setInsertCursor
Maybe I didn't follow your question correctly but what's wrong with using 'insert...select' where the select joins all these tables?
It sounds like you are trying to implement precedence. I question whether you actually need the original table. If not, just use not exists
insert into . . .
select . . .
from Table1;
insert into . . .
select . . .
from Table2
where not exists (select 1
from Table1
where Table1.userid = Table2.userid and
Table1.colname = Table2.colname
);
insert into . . .
select . . .
from Table3
where not exists (select 1
from Table1
where Table1.userid = Table3.userid and
Table1.colname = Table3.colname
) and
not exists (select 1
from Table2
where Table2.userid = Table3.userid and
Table2.colname = Table3.colname
);
If you are actually conditionally choosing the columns from each table, then you might have to construct the above as dynamic SQL.
If you add ID columns in each table and index it you can make selection a lot faster. Give unique index for ID column and that's it. You can improve the speed of entire action a lot.
This week, I found myself in need of some dynamic queries. Now, dynamic queries and dynamic where clauses are nothing new and well documented all over the web. Yet, I needed something more. I needed a fluid way of pulling new where fields to the client and allowing the users to make as many filters as needed. Even have multiple filters on a single field. Even more so, I needed to have access to all the possible operators within SQL server. The following is code is one way to make this happen. I will attempt to point out highlights of the code with the complete code at the bottom.
Hope you enjoy the code.
REQUIREMENTS
The solution will never allow SQL injections. (No exec(command) can be used)
The caller of the stored procedure could be anything.
The data set must come from a Stored Procedure.
Any field can be filtered as many times as needed, with just about any operation.
Any combination of filters should be allowed.
The stored procedure should allow for mandatory parameters
First, let us look over the parameters.
CREATE PROCEDURE [dbo].[MyReport]
-- Add the parameters for the stored procedure here
#p_iDistributorUID INT, -- manditory
#p_xParameters XML = null --optional parameters (hostile)
The first parameter must always be sent, in this demo we have a distributor id that must be sent in. The second parameter is an XML document. These are the “Dynamic Where Clauses” and we consider these potential sql injections, or as I perceive this parameter as hostile.
<root>
<OrFilters>
<AndFilter Name="vcinvoicenumber" Operator="2" Value="inv12"/>
<AndFilter Name="vcID" Operator="1" Value="asdqwe"/>
</OrFilters>
<OrFilters>
<AndFilter Name="iSerialNumber" Operator="1" Value="123456"/>
</OrFilters>
NAME= field name(you could just use the object_id if you want to obfuscate)
OPERATOR = SQL operators such as <,>,=,like,ect.
VALUE is what the users has entered.
Here is what the final code would look like.
Select *
FROM someTable
Where (
vcinvoicenumber like ‘inv12%’
and vcID = ‘asdqwe’
)
Or
(
iSerialNumber = ‘123456’
)
First thing is to find out how many “OrFilters” tags there are.
SELECT #l_OrFilters = COUNT(1)
FROM #p_xParameters.nodes('/root/OrFilters') Tab(Col)
Next we need a temp table to hold the values in the XML doc.
CREATE TABLE #temp
(keyid int IDENTITY(1,1) NOT NULL,value varchar(max))
We now create a cursor for the first “OrFilters”tag.
DECLARE OrFilter_cursor CURSOR LOCAL
FOR
SELECT Tab.Col.value('#Name','varchar(max)') AS Name
,Tab.Col.value('#Operator','Smallint') AS Operator
,Tab.Col.value('#Value','varchar(max)') AS Value
FROM #p_xParameters.nodes('/root/OrFilters[sql:variable("#l_OrFilters")]/AndFilter') Tab(Col)
To make sure we have a valid field, we check against the system tables.
SELECT #l_ParameterInName = [all_columns].Name
,#l_ParameterDataType= [systypes].Name
,#l_ParameterIsVariable= Variable
,#l_ParameterMax_length=max_length
,#l_ParameterpPrecision=precision
,#l_ParameterScale =[all_columns].scale
FROM [AprDesktop].[sys].[all_views]
INNER JOIN [AprDesktop].[sys].[all_columns]
ON [all_views].object_id = [all_columns].object_id
INNER JOIN [AprDesktop].[sys].[systypes]
ON [all_columns].system_type_id = [systypes].xtype
WHERE [all_views].name = 'vw_CreditMemo_Lists'
and [all_columns].Name= #l_Name
Now we save the parameter to the temp table
IF ##ROWCOUNT = 1
BEGIN
INSERT INTO #temp (value) SELECT #l_Value
SET #l_FilterKey = ##IDENTITY
.
.
.
We make a call to a function that will actually build the where clauses.
SET #l_TemporaryWhere +=
dbo.sfunc_FilterWhereBuilder2(#l_Operator
,#l_ParameterInName
,#l_TemporaryWhere
,CAST(#l_FilterKey AS VARCHAR(10))
,#l_ParameterDataType
,#l_ParameterVariable)
Looking at this Function, you can see we used a case statement to genereate the where clause string.
set #l_CastToType = ' CAST( VALUE as ' + #p_DataType + #p_PrecisionScale + ') '
set #l_CastToString = ' CAST( '+#p_Field+' as VARCHAR(MAX)) '
-- Add the T-SQL statements to compute the return value here
SELECT #l_Return =
CASE
--EQUAL
--ex: vcUID = (select value FROM #temp where keyid = 1)
WHEN #p_Command = 1
THEN #p_Field + ' = (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--BEGIN WITH
--ex:vcInvoiceNumber LIKE (select value+'%' FROM #temp where keyid = 2)
WHEN #p_Command = 2
THEN #l_CastToString +' LIKE (select value+'+ QUOTENAME('%','''') +' FROM #temp where keyid = ' + #p_KeyValue + ')'
.
.
.
And finally call the sp_execute.
EXECUTE sp_executesql #l_SqlCommand ,#l_Parameters, #p_iDistributorUID
CALLING CODE
DECLARE #return_value int
DECLARE #myDoc xml
SET #myDoc =
'<root>
<OrFilters>
<AndFilter Name="vcinvoicenumber" Operator="1" Value="123"/>
</OrFilters>
</root>'
EXEC #return_value = [dbo].[spp_CreditMemo_Request_List_v2]
#p_siShowView = 1,
#p_iDistributorUID = 3667,
#p_xParameters = #myDoc
SELECT 'Return Value' = #return_value
MAIN STORED PROCEDURE
ALTER PROCEDURE [dbo].[MyReport]
-- Add the parameters for the stored procedure here
#p_iDistributorUID INT , --manditory
#p_xParameters XML = null --optional parameters(hostile)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #l_TemporaryWhere NVARCHAR(MAX)
-- declare variables
DECLARE #l_SqlCommand NVARCHAR(MAX)
DECLARE #l_Parameters NVARCHAR(MAX)
DECLARE #l_WhereClause NVARCHAR(MAX)
DECLARE #l_OrFilters INT
--cursor variables
DECLARE #l_Name VARCHAR(MAX)
DECLARE #l_Operator SMALLINT
DECLARE #l_Value VARCHAR(MAX)
--variables from the database views
DECLARE #l_ParameterInName NVARCHAR(128)
DECLARE #l_ParameterDataType NVARCHAR(128)
DECLARE #l_ParameterIsVariable BIT
DECLARE #l_ParameterMax_length SMALLINT
DECLARE #l_ParameterpPrecision TINYINT
DECLARE #l_ParameterScale TINYINT
--the variable that holds the latest ##identity
DECLARE #l_FilterKey INT
--init local variables
SET #l_SqlCommand =''
SET #l_Parameters =''
SET #l_WhereClause =''
BEGIN TRY
--verify manditory variables
if #p_iDistributorUID is null
raiserror('Null values not allowed for #p_iDistributorUID', 16, 1)
--Build the base query
-- only the fields needed in the tile should be selected
SET #l_SqlCommand =
' SELECT * ' +
' FROM vw_Lists '
--how many "OR" filters are there
SELECT #l_OrFilters = COUNT(1)
FROM #p_xParameters.nodes('/root/OrFilters') Tab(Col)
--create a temp table to
--hold the parameters to send into the sp
CREATE TABLE #temp
(
keyid int IDENTITY(1,1) NOT NULL,value varchar(max)
)
--Cycle through all the "OR" Filters
WHILE #l_OrFilters > 0
BEGIN
SET #l_TemporaryWhere = '';
--Create a cursor of the Next "OR" filter
DECLARE OrFilter_cursor CURSOR LOCAL
FOR
SELECT Tab.Col.value('#Name','varchar(max)') AS Name
,Tab.Col.value('#Operator','Smallint') AS Operator
,Tab.Col.value('#Value','varchar(max)') AS Value
FROM #p_xParameters.nodes('/root/OrFilters[sql:variable("#l_OrFilters")]/AndFilter') Tab(Col)
OPEN OrFilter_cursor
FETCH NEXT FROM OrFilter_cursor
INTO #l_Name, #l_Operator,#l_Value
WHILE ##FETCH_STATUS = 0
BEGIN
--verify the parameter actual exists
-- and get parameter details
SELECT #l_ParameterInName = [all_columns].Name
,#l_ParameterDataType= [systypes].Name
,#l_ParameterIsVariable= Variable
,#l_ParameterMax_length=max_length
,#l_ParameterpPrecision=precision
,#l_ParameterScale =[all_columns].scale
FROM [AprDesktop].[sys].[all_views]
INNER JOIN [sys].[all_columns]
ON [all_views].object_id = [all_columns].object_id
INNER JOIN [sys].[systypes]
ON [all_columns].system_type_id = [systypes].xtype
WHERE [all_views].name = 'vw_CreditMemo_Lists'
and [all_columns].Name= #l_Name
--if the paremeter exists, create a where clause
-- if the parameters does not exists, possible injection
IF ##ROWCOUNT = 1
BEGIN
--insert into the temp table the parameter value
--NOTE: we have turned in the ##identity as the key
INSERT INTO #temp (value) SELECT #l_Value
SET #l_FilterKey = ##IDENTITY
-- if the parameter is variable in length, add the length
DECLARE #l_ParameterVariable VARCHAR(1000)
IF #l_ParameterIsVariable = 1
BEGIN
SET #l_ParameterVariable ='(' + CAST(#l_ParameterMax_length as VARCHAR(MAX)) + ') '
END
ELSE
BEGIN
SET #l_ParameterVariable = ''
END
-- create the where clause for this filter
SET #l_TemporaryWhere +=
dbo.sfunc_FilterWhereBuilder2(#l_Operator
,#l_ParameterInName
,#l_TemporaryWhere
,CAST(#l_FilterKey AS VARCHAR(10))
,#l_ParameterDataType
,#l_ParameterVariable)
END
FETCH NEXT FROM OrFilter_cursor
INTO #l_Name, #l_Operator,#l_Value
END
-- clean up the cursor
CLOSE OrFilter_cursor
DEALLOCATE OrFilter_cursor
--add the and filers
IF #l_TemporaryWhere != ''
BEGIN
--if the where clause is not empty, we need to add an OR
IF #l_WhereClause != ''
BEGIN
SET #l_WhereClause += ' or ';
END
--add temp to where clause including the
SET #l_WhereClause += '(' + #l_TemporaryWhere + ')';
END
--get the next AND set
SET #l_OrFilters = #l_OrFilters - 1
END
--generate the where clause
IF #l_WhereClause != ''
BEGIN
SET #l_WhereClause ='('+ #l_WhereClause + ') AND '
END
--add in the first mandatory parameter
SET #l_WhereClause += ' vw_CreditMemo_Lists.iDistributorUID = #l_iDistributorUID '
SET #l_Parameters += '#l_iDistributorUID int'
--do we need to attach the where clause
if #l_WhereClause IS NOT NULL AND RTRIM(LTRIM(#l_WhereClause)) != ''
BEGIN
SET #l_SqlCommand += ' WHERE '+ #l_WhereClause;
END
print #l_SqlCommand
--query for the data
EXECUTE sp_executesql #l_SqlCommand ,#l_Parameters, #p_iDistributorUID
END TRY
BEGIN CATCH
DECLARE #ErrorUID int;
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
--write the to stored procedure log
EXEC #ErrorUID = spp_Errors_CreateEntry #l_SqlCommand
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorUID, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
IF(CURSOR_STATUS('LOCAL','OrFilter_cursor') >= 0)
BEGIN
CLOSE OrFilter_cursor
END
IF(CURSOR_STATUS('LOCAL','OrFilter_cursor') = -1)
BEGIN
DEALLOCATE OrFilter_cursor
END
END CATCH
return
END
FUNCTION
ALTER FUNCTION [dbo].[sfunc_FilterWhereBuilder2]
(
#p_Command SMALLINT ,
#p_Field VARCHAR(1000) ,
#p_WhereClause VARCHAR(MAX) ,
#p_KeyValue VARCHAR(10) ,
#p_DataType VARCHAR(100) = NULL ,
#p_PrecisionScale VARCHAR(100) = NULL
)
RETURNS VARCHAR(MAX)
AS
BEGIN
-- Declare the return variable here
DECLARE #l_Return VARCHAR(MAX)
DECLARE #l_CastToType VARCHAR(4000)
DECLARE #l_CastToString VARCHAR(MAX)
set #l_CastToType = ' CAST( VALUE as ' + #p_DataType + #p_PrecisionScale + ') '
set #l_CastToString = ' CAST( '+#p_Field+' as VARCHAR(MAX)) '
-- Add the T-SQL statements to compute the return value here
SELECT #l_Return =
CASE
--EQUAL
--ex: vcBurnUID = (select value FROM #temp where keyid = 1)
WHEN #p_Command = 1
THEN #p_Field + ' = (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--BEGIN WITH
--ex:vcInvoiceNumber LIKE (select value+'%' FROM #temp where keyid = 2)
WHEN #p_Command = 2
THEN #l_CastToString +' LIKE (select value+'+ QUOTENAME('%','''') +' FROM #temp where keyid = ' + #p_KeyValue + ')'
--END WITH
--ex:vcInvoiceNumber LIKE (select '%'+value FROM #temp where keyid = 2)
WHEN #p_Command = 4
THEN #l_CastToString +' LIKE (select '+ QUOTENAME('%','''') +'+value FROM #temp where keyid = ' + #p_KeyValue + ')'
--END WITH
--ex:vcInvoiceNumber LIKE (select '%'+value+'%' FROM #temp where keyid = 2)
WHEN #p_Command = 8
THEN #l_CastToString +' LIKE (select '+ QUOTENAME('%','''') +'+value+'+ QUOTENAME('%','''') +' FROM #temp where keyid = ' + #p_KeyValue + ')'
--greater than
--ex: iSerialNumber > (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 16
THEN #p_Field +' > (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--greater than equal
--ex: iSerialNumber >= (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 32
THEN #p_Field +' >= (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--Less than
--ex: iSerialNumber < (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 64
THEN #p_Field +' < (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--less than equal
--ex: iSerialNumber <= (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 128
THEN #p_Field +' <= (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--less than equal
--ex: iSerialNumber != (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 256
THEN #p_Field +' != (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--default to an empty string
ELSE ''
END
if #l_Return != '' and LEN(#p_WhereClause) > 1
begin
set #l_Return = ' AND ' + #l_Return
end
-- Return the result of the function
RETURN #l_Return
END
I know you can create a script to replicate a table using:
right click table > script table as > create to > new query editor window
But how can I generate a script that contains a bunch of insert commands for each row in the table?
Table1
Id1, Row1
Id2, Row2
Id3, Row3
Insert into Table1 values(Row1);
Insert into Table1 values(Row2);
Insert into Table1 values(Row3);
I ended up doing this
right click database > Tasks > Generate Scripts ... > selected the tables > in the advanced options I set "Types of data to script" to "Schema and data"
Select
'Insert into Table (
IntField1
StringField2
Column3)
values (' +
IntField1 + ',' +
+ '''' + StringField2 + ''',' +
Column2 + ')' as InsertQuery
From Table
Something like this, just remember if your string contains a single quote you will need to make sure you replace it like this replace(stringfield, '''', '''''')
So this isnt super pretty cuz I kind of took one of my sp's and hacked it up for this. But basically this will take any table and print a series of insert statements into a table called tbl_text (which you would need to create)
The arguments are the table name and the table ID from sysobjects
--this is how you get the tbl_id
SELECT id FROM sysobjects WHERE type = 'U' AND name = 'tablename'
CREATE PROCEDURE dbo.sp_export_table
#tblhdr varchar(100),
#tblID varchar(100)
AS
SET NOCOUNT ON
IF object_id('tempdb..##temptable') IS NOT NULL
BEGIN
DROP TABLE ##temptable
END
DECLARE #identity bit
DECLARE #typestmt nvarchar(100)
DECLARE #typeval int
DECLARE #rowstmt nvarchar(1000)
DECLARE #rowID varchar(50)
DECLARE #orderby nvarchar(100)
DECLARE #clmnstmt varchar(200)
DECLARE #clmnhdr varchar(50)
DECLARE #clmnstring varchar(1000)
DECLARE #valuestmt nvarchar(200)
DECLARE #valuestring nvarchar(3000)
DECLARE #value nvarchar(1000)
DECLARE #insertstmt varchar(1000)
DECLARE #params nvarchar(100)
DECLARE #param2 nvarchar(100)
SELECT #rowstmt = N'SELECT TOP 1 #inside_var = name FROM syscolumns WHERE id = ' + #tblID + ' ORDER BY colorder'
SELECT #params = N'#inside_var NVARCHAR(1000) OUTPUT'
EXEC sp_executesql #rowstmt, #params, #inside_var = #orderby OUTPUT
SELECT #rowstmt = 'SELECT *, ROW_NUMBER() OVER (ORDER BY ' + #orderby + ') AS row INTO ##temptable FROM ' + #tblhdr
exec(#rowstmt)
IF object_id('tempdb..##temptable') IS NOT NULL
BEGIN
DECLARE row_cursor CURSOR FOR
SELECT row FROM ##temptable
OPEN row_cursor
FETCH NEXT FROM row_cursor
INTO #rowID
--if table has identity and has records write identity_insert on
SET #identity = 0
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE OBJECTPROPERTY(OBJECT_ID(TABLE_NAME),
'TableHasIdentity') = 1 AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME = #tblhdr) AND EXISTS(SELECT * FROM ##temptable)
BEGIN
SET #identity = 1
INSERT INTO dbo.tbl_text VALUES('SET IDENTITY_INSERT dbo.' + #tblhdr + ' ON')
END
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #clmnstmt = 'DECLARE column_cursor CURSOR FOR SELECT name FROM syscolumns WHERE id = ' + #tblID + ' ORDER BY colorder'
exec(#clmnstmt)
OPEN column_cursor
FETCH NEXT FROM column_cursor
INTO #clmnhdr
SELECT #clmnstring = '('
SELECT #valuestring = '('
WHILE ##FETCH_STATUS = 0
BEGIN
IF #clmnhdr <> 'row'
BEGIN
SELECT #clmnstring = #clmnstring + #clmnhdr + ','
SELECT #valuestmt = N'SELECT #inside_var = ' + #clmnhdr + ' FROM ##temptable WHERE row = ' + #rowID
EXEC sp_executesql #valuestmt, #params, #inside_var = #value OUTPUT
SELECT #typestmt = N'SELECT #inside_var2 = xtype FROM syscolumns WHERE name = ''' + #clmnhdr + ''' AND id = ' + #tblID
SELECT #param2 = N'#inside_var2 INT OUTPUT'
EXEC sp_executesql #typestmt, #param2, #inside_var2 = #typeval OUTPUT
IF #typeval NOT IN (48,52,56,59,60,62,104,108,122,127)
BEGIN
SET #value = REPLACE(#value,'''','''''')
SET #value = '''' + #value + ''''
SET #value = ISNULL(#value, '''''')
END
IF NOT (#typeval = 34)
BEGIN
SELECT #valuestring = #valuestring + #value + ','
END
ELSE
BEGIN
SELECT #valuestring = #valuestring + '''''' + ','
END
END
FETCH NEXT FROM column_cursor
INTO #clmnhdr
END
SET #clmnstring = LEFT(#clmnstring, LEN(#clmnstring) - 1)
SET #valuestring = LEFT(#valuestring, LEN(#valuestring) - 1)
INSERT INTO dbo.tbl_text VALUES('INSERT INTO dbo.' + #tblhdr + ' ' + #clmnstring + ') VALUES' + #valuestring + ')')
FETCH NEXT FROM row_cursor
INTO #rowID
CLOSE column_cursor
DEALLOCATE column_cursor
END
--if it wrote identity_insert on, turn it off
IF (#identity = 1)
BEGIN
INSERT INTO dbo.tbl_text VALUES('SET IDENTITY_INSERT dbo.' + #tblhdr + ' OFF')
END
CLOSE row_cursor
DEALLOCATE row_cursor
END
IF object_id('tempdb..##temptable') IS NOT NULL
BEGIN
DROP TABLE ##temptable
END
GO
If you've got an account on SSC, you can use the script I published last year. It works without cursors an it enables custom filtering.
http://www.sqlservercentral.com/scripts/Script+Data/65998/
Hope this helps
Assuming Row is an INT NOT NULL. You could write a SELECT statement that outputs SQL;
SELECT N'INSERT INTO Table1 VALUES (' + CAST(Row AS NVARCHAR(10)) + N');'
FROM Table1
Then output your results to text.