Aim: I want to query three tables in total and display each line separately.
I only need to display results from TblA and TblF as TblProperty is the parent table so whilst we might search using it I don't need it's data.
i.e. A user might search for a Postcode however a user might only search for a rating in TblA.
I've provided two pieces of code. The first is a cut down version, I think this might help guide both the reader and myself to the solution. The second code is the full version. (I need to add some quotenames etc.. but whilst I'm testing I'm after getting the main part working)
The main point: If I have a one result from TblA and one result from TblF I want two lines of data not one returned.
Using:
SQL Server Management Studio 2012
Query:
I'm looking to get a fresh pair of eyes at this stage. Maybe I need to search both tables first and then the property or look to create a temporary table?
Code 1:
USE DB
DECLARE #QUERY NVARCHAR(MAX) = ''
DECLARE #QUERYSTRING NVARCHAR(MAX) = ''
DECLARE #sTypeOfUtility NVARCHAR(MAX) = '2'
SET #QUERY =
'SELECT
p.ID AS ID,
p.UPRN AS UPRN,
COALESCE(a.OverallRiskCategory,''0'') AS RiskType2,
COALESCE(f.RiskRating,''0'') AS RiskType3,
COALESCE(a.TypeOfUtility,'''') + COALESCE(f.TypeOfUtility,'''') AS TypeOfUtility
FROM TblProperty AS p'
SET #QUERY = #QUERY + ' INNER JOIN TblA AS a on a.UPRN = p.UPRN'
SET #QUERY = #QUERY + ' INNER JOIN TblFAS f on f.FIREUPRN = p.UPRN'
IF #sTypeOfUtility = '2'
SET #QUERYSTRING = #QUERYSTRING + ' AND a.TypeOfUtility LIKE ''%' + LTRIM(RTRIM(#sTypeOfUtility)) + '%'''
IF #sTypeOfUtility = '3'
SET #QUERYSTRING = #QUERYSTRING + ' AND f.TypeOfUtility LIKE ''%' + LTRIM(RTRIM(#sTypeOfUtility)) + '%'''
SET #QUERY = LTRIM(RTRIM(#QUERY)) + ' WHERE 1 = 1 ' + LTRIM(RTRIM(#QUERYSTRING)) + ' ORDER BY typeofutility DESC'
EXECUTE(#QUERY)
Code 2 (Fullcode so far but with only two tables):
USE [DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
ALTER PROCEDURE [dbo].[spGridSearch]
#sRiskRating NVARCHAR(50),#sUPRN NVARCHAR(20),
#sPostcode VARCHAR(20), #sPropertyName NVARCHAR(50) ,
#sStreet NVARCHAR(50), #sTypeOfUtility NVARCHAR(10),
#sDateFrom DATETIME, #sDateTo DATETIME
AS
BEGIN
DECLARE #QUERY NVARCHAR(MAX) = ''
DECLARE #QUERYSTRING NVARCHAR(MAX) = ''
SET #QUERY =
'SELECT
p.ID AS ID,
p.UPRN AS UPRN,
COALESCE(a.OverallRiskCategory,''0'') AS OverallRiskCategory,
COALESCE(a.TypeOfUtility,''0'') AS TypeOfUtility,
COALESCE(a.SurveyDate,'''') AS SurveyDate, COALESCE(a.ItemRef, '''') AS ItemRef,
COALESCE(a.NextSurveyDue,'''') AS NextSurveyDue ,
COALESCE(a.Recommendations,''NO DATA'') AS Recommendations,
COALESCE(a.StatusOfIssue,''0'') As StatusOfIssue
FROM TblProperty AS p '
SET #QUERY = #QUERY + ' LEFT JOIN TblA AS a on p.UPRN = a.UPRN '
IF #sRiskRating <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' AND a.OverallRiskCategory LIKE ''%' + LTRIM(RTRIM(#sRiskRating)) + '%'''
IF #sTypeOfUtility <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' AND a.TypeOfUtility LIKE ''%' + LTRIM(RTRIM(#sTypeOfUtility)) + '%'''
--IF #sDateFROM <> '2050-01-01' AND #sDateTO <> '2050-01-01'
--SET #QUERYSTRING = #QUERYSTRING + ' AND a.SurveyDate BETWEEN ' + #sDateFrom + ' AND ' + #sDateTo
IF #sUPRN <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' AND p.UPRN LIKE ''%' + LTRIM(RTRIM(#sUPRN)) + '%'''
IF #sPostcode <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' AND p.Postcode LIKE ''%' + LTRIM(RTRIM(#sPostcode)) + '%'''
IF #sPropertyName <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' AND p.BuildingNo LIKE ''%' + LTRIM(RTRIM(#sPropertyName)) + '%'''
IF #sStreet <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' AND p.Street LIKE ''%' + LTRIM(RTRIM(#sStreet)) + '%'''
IF LEN(LTRIM(RTRIM(#QUERYSTRING))) > 5
--Remove last as we dont need it
--SET #QUERYSTRING = LEFT(#QUERYSTRING, NULLIF(LEN(#QUERYSTRING)-1,-1))
SET #QUERY = LTRIM(RTRIM(#QUERY)) + ' WHERE 1 = 1 ' + LTRIM(RTRIM(#QUERYSTRING))
EXECUTE(#QUERY)
END
References:
http://support.sas.com/documentation/cdl/en/sqlproc/62086/HTML/default/viewer.htm#a001361784.htm
http://www.w3schools.com/sql/sql_join.asp
As I mentioned in the comments you should use the UNION ALL statement to get all results from both tables. This requires that both select have the same column count and columns should have the same datatype
You query would basically look like this:
SELECT
...
FROM TblProperty AS p
INNER JOIN TblA AS a on a.UPRN = p.UPRN
UNION ALL
SELECT
...
FROM TblProperty AS p
INNER JOIN TblFAS f on f.FIREUPRN = p.UPRN
I would also recommend to use use sp_executesql and named parameters like this named parameters in sp_executesql
Related
This is a progression from the question asked here: How to SELECT and UNION from a group of Tables in the schema in SQL Server 2008 R2
I would like to do very much the same thing and the answer given by MarkD works perfectly for the database I am currently working with. Although admittedly I'd like to understand exactly how. How does the query below build the union query from the list of tables returned by the information_schema?
DECLARE #Select_Clause varchar(600) = N'SELECT [Patient_Number] AS [ID number]
,[Attendance Date] AS [Date Seen]
,[Attendance_Type] AS [New/Follow up]
,[Episode Type] AS [Patient Type]
,[Local Authority District]
,Postcode, N''Shaw'' AS Clinic '
,#Where_Clause varchar(100) = N' WHERE [EPISODE TYPE] LIKE N''HIV'''
,#Union_Clause varchar(100) = N' UNION ALL '
,#Query nvarchar(max) = N''
,#RawDataBase varchar(50) = N'BHT_1819_RawData'
,#Schema varchar(50) = N'HIVGUM'
,#Table_Count tinyint;
DECLARE #Table_Count_def nvarchar(100) = N'#TableSchema varchar(50)
,#Table_CountOUT tinyint OUTPUT'
,#Start_Position int = LEN(REPLACE(#Select_Clause, N' ', N'-'))
,#Length int;
SET #Query = N'SELECT #Table_CountOUT = COUNT(*) FROM ' + #RawDataBase +
N'.INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE #TableSchema';
EXEC sp_executesql #query, #Table_Count_def, #TableSchema=#Schema,
#Table_CountOUT=#Table_Count OUTPUT;
SET #Query = N'';
IF #Table_Count > 0
Begin
IF OBJECT_ID(N'dbo.HIV_Cumulative', N'U') is not null
DROP TABLE dbo.HIV_Cumulative;
SELECT #Query = #Query + #Select_Clause + N' FROM ' + #RawDataBase +
N'.HIVGUM.' + TABLE_NAME + #Where_Clause + #Union_Clause
FROM BHT_1819_RawData.INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA LIKE #Schema;
SET #Length = LEN(REPLACE(#query, N' ', N'-')) - #Start_Position -
LEN(REPLACE(#Where_Clause + #Union_Clause, N' ', N'-'));
SELECT #Query = SUBSTRING(#QUERY , #Start_Position+1, #Length)
SET #Query = #Select_Clause + N' INTO BHT_SLR..HIV_Cumulative ' + #QUERY
+ #Where_Clause;
EXEC sp_executesql #Query
End
ELSE
PRINT N'No tables present in database ' + #RawDataBase + N' for Schema ' +
#Schema + N'. You must import source data first.';
The added complication is that I am querying the tables on a separate DB - currently BHT_1819_RawData - so have hard coded the database where it queries the information_schema. What I would really like to do is to specify the separate database using a variable. So that it can be reconfigured to extract from BHT_1920_RawData. I am fairly familiar with exec and sp_executesql, but have only occasionally used output parameters so am not sure what is required here. The attempts that I have made haven't worked. Once I have got this right, I will need to create several other similar scripts that work on the same principle.
Once I realised what needed to happen, I went through some trial and error and came up with a solution:
SET #ParmDef = N'#QueryOut nvarchar(2500) OUTPUT';
SET #sql_string = N'SELECT #QueryOut = #QueryOut + N'''
+ #Select_Clause + ' FROM '
+ #RawDataBase
+ N'.[' + #Schema + N'].'' + TABLE_NAME + N'' '
+ #Where_Clause
+ #Union_Clause
+ N''' FROM '
+ #RawDataBase
+ N'.INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE '''
+ #Schema
+ N''' AND TABLE_NAME NOT LIKE N''%_YTD%''';
EXEC sp_executesql #sql_string, #ParmDef, #QueryOut=#Query OUTPUT;
SET #Length = LEN(REPLACE(#query, N' ', N'-')) - #Start_Position -
LEN(REPLACE(#Where_Clause + #Union_Clause, N' ', N'-'));
SELECT #Query = SUBSTRING(#QUERY , #Start_Position, #Length+1);
SET #Query = REPLACE(#Select_Clause, N'''''', '''') + N' INTO ' + #New_Table + N' ' +
#QUERY + REPLACE(#Where_Clause, N'''''', '''');
EXEC sp_executesql #Query;
hello this my dynamic query and this procedure I did tested is working.
but Does not bring data to the server-side (entity)
visual studio 2012
framework 4.5
entity store procedure
public IEnumerable<spGetInvoiceDetailSearch_Result> GetInvoiceDetailedSearch(InvoiceModel item)
{
return DALContext.GetInvoiceDetailedSearch(item);
}
ALTER PROCEDURE [dbo].[spGetInvoiceDetailSearch] #InvoiceItemID INT
,#InvoiceTypeID INT
,#VesselID INT
,#PaidBy NVARCHAR(50)
,#InvoiceNo NVARCHAR(50)
,#CompanyID INT
,#InvoiceFromDate DATE
,#InvoiceToDate DATE
,#FromDueDate DATE
,#ToDueDate DATE
,#FromAmount DECIMAL(18, 4)
,#ToAmount DECIMAL(18, 4)
,#DueDateType NVARCHAR(50)
AS
BEGIN
DECLARE #SQLQuery AS NVARCHAR(4000)
SELECT #SQLQuery =
'SELECT dbo.Invoices.InvoiceID, dbo.Invoices.CompanyID, dbo.Invoices.VesselID, dbo.Invoices.InvoiceNo, dbo.Invoices.DueDate, dbo.Invoices.Amount,
dbo.Invoices.Comment, dbo.Invoices.IsPaid, dbo.Invoices.PaymentDate, dbo.Invoices.PaidBy, dbo.Invoices.Period, dbo.Invoices.InvoiceDate,
dbo.Invoices.InvoiceCurrencyCode, dbo.Invoices.InvoiceAmount, dbo.Invoices.IsReceived, dbo.Invoices.IsProforma, dbo.Invoices.InvoiceTypeID,
dbo.Invoices.IsDeleted, dbo.Invoices.Parity, dbo.Invoices.DueDateType, dbo.Vessels.Name AS VesselName, dbo.InvoiceVsInvoiceItems.ItemPrice as ItemPrice,
dbo.InvoiceVsInvoiceItems.InvoiceItemID as InvoiceItemID, dbo.InvoiceVsInvoiceItems.VAT as VAT, dbo.InvoiceVsInvoiceItems.ItemType as ItemType, dbo.InvoiceItems.Name AS InvoiceItemName,
dbo.Companies.Name AS CompanyName, dbo.InvoiceTypes.Name AS InvoiceTypeName
FROM dbo.Invoices LEFT OUTER JOIN
dbo.Companies ON dbo.Invoices.CompanyID = dbo.Companies.CompanyID LEFT OUTER JOIN
dbo.InvoiceTypes ON dbo.Invoices.InvoiceTypeID = dbo.InvoiceTypes.InvoiceTypeID LEFT OUTER JOIN
dbo.InvoiceVsInvoiceItems ON dbo.Invoices.InvoiceID = dbo.InvoiceVsInvoiceItems.InvoiceID LEFT OUTER JOIN
dbo.InvoiceItems ON dbo.InvoiceVsInvoiceItems.InvoiceVsInvoiceItemID = dbo.InvoiceItems.InvoiceItemID LEFT OUTER JOIN
dbo.Vessels ON dbo.Invoices.VesselID = dbo.Vessels.VesselID WHERE
dbo.Invoices.IsDeleted != 1
and dbo.Vessels.IsDeleted != 1
and dbo.Companies.IsDeleted != 1 '
SET FMTONLY OFF
IF #InvoiceItemID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.InvoiceItems.InvoiceItemID= ''' + CAST(#InvoiceItemID AS NVARCHAR(50)) + ''''
END
IF #InvoiceTypeID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceTypeID= ''' + CAST(#InvoiceTypeID AS NVARCHAR(50)) + ''''
END
IF #VesselID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.VesselID= ''' + CAST(#VesselID AS NVARCHAR(50)) + ''''
END
IF #PaidBy IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + 'AND dbo.Invoices.PaidBy = ''' + CAST(#PaidBy AS NVARCHAR(50)) + ''''
END
IF #InvoiceNo IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + 'AND dbo.Invoices.InvoiceNo = ''' + CAST(#InvoiceNo AS NVARCHAR(50)) + ''''
END
IF #CompanyID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.CompanyID = ''' + CAST(#CompanyID AS NVARCHAR(50)) + ''''
END
IF #FromAmount IS NOT NULL AND #ToAmount IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.Amount BETWEEN ''' + CAST(#FromAmount AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToAmount AS NVARCHAR(100)) + ''''
END
IF #DueDateType IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + 'AND dbo.Invoices.DueDateType = ''' + CAST(#DueDateType AS NVARCHAR(50)) + ''''
END
IF #InvoiceFromDate IS NOT NULL AND #InvoiceToDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceDate Between ''' + CAST(#InvoiceFromDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#InvoiceToDate AS NVARCHAR(100)) + ''''
END
IF #FromDueDate IS NOT NULL AND #ToDueDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.DueDate Between ''' + CAST(#FromDueDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToDueDate AS NVARCHAR(100)) + ''''
END
EXECUTE (#SQLQuery)
END
and end question
my table date type : date format but
server shows it like datetime how can I do to change it to date format..
thank you
regards
ALTER PROCEDURE [dbo].[spGetInvoiceDetailSearch] #InvoiceItemID INT
,#InvoiceTypeID INT
,#VesselID INT
,#PaidBy NVARCHAR(50)
,#InvoiceNo NVARCHAR(50)
,#CompanyID INT
,#InvoiceFromDate DATE
,#InvoiceToDate DATE
,#FromDueDate DATE
,#ToDueDate DATE
,#FromAmount DECIMAL(18, 4)
,#ToAmount DECIMAL(18, 4)
,#DueDateType NVARCHAR(50)
AS
BEGIN
DECLARE #SQLQuery AS NVARCHAR(4000)
SELECT #SQLQuery =
'SELECT dbo.Invoices.InvoiceID, dbo.Invoices.CompanyID, dbo.Invoices.VesselID, dbo.Invoices.InvoiceNo, dbo.Invoices.DueDate, dbo.Invoices.Amount,
dbo.Invoices.Comment, dbo.Invoices.IsPaid, dbo.Invoices.PaymentDate, dbo.Invoices.PaidBy, dbo.Invoices.Period, dbo.Invoices.InvoiceDate,
dbo.Invoices.InvoiceCurrencyCode, dbo.Invoices.InvoiceAmount, dbo.Invoices.IsReceived, dbo.Invoices.IsProforma, dbo.Invoices.InvoiceTypeID,
dbo.Invoices.IsDeleted, dbo.Invoices.Parity, dbo.Invoices.DueDateType, dbo.Vessels.Name AS VesselName, dbo.InvoiceVsInvoiceItems.ItemPrice as ItemPrice,
dbo.InvoiceVsInvoiceItems.InvoiceItemID as InvoiceItemID, dbo.InvoiceVsInvoiceItems.VAT as VAT, dbo.InvoiceVsInvoiceItems.ItemType as ItemType, dbo.InvoiceItems.Name AS InvoiceItemName,
dbo.Companies.Name AS CompanyName, dbo.InvoiceTypes.Name AS InvoiceTypeName
FROM dbo.Invoices LEFT OUTER JOIN
dbo.Companies ON dbo.Invoices.CompanyID = dbo.Companies.CompanyID LEFT OUTER JOIN
dbo.InvoiceTypes ON dbo.Invoices.InvoiceTypeID = dbo.InvoiceTypes.InvoiceTypeID LEFT OUTER JOIN
dbo.InvoiceVsInvoiceItems ON dbo.Invoices.InvoiceID = dbo.InvoiceVsInvoiceItems.InvoiceID LEFT OUTER JOIN
dbo.InvoiceItems ON dbo.InvoiceVsInvoiceItems.InvoiceVsInvoiceItemID = dbo.InvoiceItems.InvoiceItemID LEFT OUTER JOIN
dbo.Vessels ON dbo.Invoices.VesselID = dbo.Vessels.VesselID WHERE
dbo.Invoices.IsDeleted != 1
and dbo.Vessels.IsDeleted != 1
and dbo.Companies.IsDeleted != 1 '
SET FMTONLY OFF
IF #InvoiceItemID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.InvoiceItems.InvoiceItemID= ' + CAST(#InvoiceItemID AS NVARCHAR(50)) + ''
END
IF #InvoiceTypeID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceTypeID= ' + CAST(#InvoiceTypeID AS NVARCHAR(50)) + ''
END
IF #VesselID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.VesselID= ' + CAST(#VesselID AS NVARCHAR(50)) + ''
END
IF #PaidBy IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.PaidBy = ''' + CAST(#PaidBy AS NVARCHAR(50)) + ''''
END
IF #InvoiceNo IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceNo = ''' + CAST(#InvoiceNo AS NVARCHAR(50)) + ''''
END
IF #CompanyID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.CompanyID = ' + CAST(#CompanyID AS NVARCHAR(50)) + ''
END
IF #FromAmount IS NOT NULL AND #ToAmount IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.Amount BETWEEN ''' + CAST(#FromAmount AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToAmount AS NVARCHAR(100)) + ''''
END
IF #DueDateType IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.DueDateType = ''' + CAST(#DueDateType AS NVARCHAR(50)) + ''''
END
IF #InvoiceFromDate IS NOT NULL AND #InvoiceToDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceDate Between ''' + CAST(#InvoiceFromDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#InvoiceToDate AS NVARCHAR(100)) + ''''
END
IF #FromDueDate IS NOT NULL AND #ToDueDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.DueDate Between ''' + CAST(#FromDueDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToDueDate AS NVARCHAR(100)) + ''''
END
PRINT (#SQLQuery)
END
First of all, debugging it's very easy. Replace EXEC(#SQLQUery) with print and then you see your actual query.
You had some sintax error ( some places where AND was missing a space in front) and also you have some interger that were treated as strings.
Try my updated procedure.
It seems that procedure is getting called properly but no rows are getting returned, to debug the exact problem you can write actual hardcoded query returning 1 or more records instead of dynamic query.
So after doing that there are two possibilities
procedure call via edmx returns data, that means parameter values are causing some problem.
Any Data is not returned.
To solve any of the problem you need to check corresponding sql query which is getting generated while calling SP via Enitity Framework.
I am looking for a script which finds and replaces all fields of type string within a DB with specified text.
The script would for example take the following parameters:
Search for: null
Replace with: empty-string
The primary string data types in SQL Server: Varchar, NVarchar, Text.
This script would then comb through all string based table data and look for in this case null and replace it with a empty string.
Ok I've put together the following code in the meantime.
-- Specify 'dbo' for all tables
DECLARE #schemaName VARCHAR(5) = 'dbo'
BEGIN
DECLARE #tableName VARCHAR(255) -- table name
DECLARE #tableID INT -- table id (aka syst.table.object_id)
DECLARE table_cursor CURSOR FOR
SELECT T.object_id AS TableID, T.name AS TableName FROM sys.tables T
INNER JOIN sys.schemas S ON S.schema_id = T.schema_id
WHERE S.name = #schemaName
OPEN table_cursor
FETCH NEXT FROM table_cursor INTO #tableID, #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
-- construct each tables queries
DECLARE #totalColumnsFound INT = (SELECT COUNT(*) FROM sys.columns C WHERE OBJECT_ID = #tableID
-- text and nvarchar column data types chosen for me (if you need more like ntext, varcahr see sys.types for their ids)
AND (C.system_type_id = 35 OR c.system_type_id = 231))
IF (#totalColumnsFound > 0)
BEGIN
DECLARE #tableUpdateQuery VARCHAR(MAX) = 'update ' + #schemaName + '.' + #tableName + ' set ';
DECLARE #columnName VARCHAR(255) -- column name
DECLARE column_cursor CURSOR FOR
SELECT C.name AS ColumnName FROM sys.columns C WHERE OBJECT_ID = #tableID
-- text and nvarchar column data types chosen for me (if you need more like ntext, varcahr see sys.types for their ids)
AND (C.system_type_id = 35 OR c.system_type_id = 231)
OPEN column_cursor
FETCH NEXT FROM column_cursor INTO #columnName
WHILE ##FETCH_STATUS = 0
BEGIN
-- construct the columns for the update query, piece by piece.
-- This is also where you can apply your logic for how to handle the string update.
-- I am trimming string and updating nulls to empty strings here.
SET #tableUpdateQuery = #tableUpdateQuery + ' ' + #columnName + ' = ltrim(rtrim(isnull(' + #columnName + ',''''))),'
FETCH NEXT FROM column_cursor INTO #columnName
END
CLOSE column_cursor
DEALLOCATE column_cursor
-- trim last comma from string
SET #tableUpdateQuery = LEFT(#tableUpdateQuery, LEN(#tableUpdateQuery) - 1)
/** debuging purposes **
print 'Updating table --> ' + #tableName
print #tableUpdateQuery
print ' '
*/
-- execute dynamic sql
EXEC(#tableUpdateQuery)
END
FETCH NEXT FROM table_cursor INTO #tableID, #tableName
END
CLOSE table_cursor
DEALLOCATE table_cursor
END
--GO
this should help you:
/*
Author: sqiller
Description: Searches for a value to replace in all columns from all tables
USE: EXEC dbo.usp_Update_AllTAbles 'work', 'sqiller', 1
#search = Value to look for Replace
#newvalue = the value that will replace #search
#Test = If set to 1, it will only PRINT the UPDATE statement instead of EXEC, useful to see
what is going to update before.
*/
CREATE PROCEDURE dbo.usp_Update_AllTAbles(
#search varchar(100),
#newvalue varchar(100),
#Test bit)
AS
BEGIN
IF NOT EXISTS (select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Tables_to_Update')
BEGIN
CREATE TABLE dbo.Tables_to_Update(
Table_name varchar(100),
Column_name varchar(100),
recordsToUpdate int
)
END
DECLARE #table varchar(100)
DECLARE #column varchar(100)
DECLARE #SQL varchar(max)
SELECT TABLE_SCHEMA+'.'+TABLE_NAME as Table_Name, 0 as Processed INTO #tables from information_schema.tables WHERE TABLE_TYPE != 'VIEW'
WHILE EXISTS (select * from #tables where processed = 0)
BEGIN
SELECT top 1 #table = table_name from #tables where processed = 0
SELECT column_name, 0 as Processed INTO #columns from information_schema.columns where TABLE_SCHEMA+'.'+TABLE_NAME = #table
WHILE EXISTS (SELECT * from #columns where processed = 0)
BEGIN
SELECT top 1 #column = COLUMN_NAME from #columns where processed = 0
SET #SQL = 'INSERT INTO Tables_to_Update
select '''+ #table +''', '''+ #column +''', count(*) from '+#table+ ' where '+ #column +' like ''%'+ #search +'%'''
EXEC(#SQL)
IF EXISTS (SELECT * FROM Tables_to_Update WHERE Table_name = #table)
BEGIN
SET #SQL = 'UPDATE '+ #table + ' SET '+ #column + ' = REPLACE('''+#column+''','''+#search+''','''+ #newvalue +''') WHERE '+ #column + ' like ''%'+#search+'%'''
--UPDATE HERE
IF (#Test = 1)
BEGIN
PRINT #SQL
END
ELSE
BEGIN
EXEC(#SQL)
END
END
UPDATE #columns SET Processed = 1 where COLUMN_NAME = #column
END
DROP TABLE #columns
UPDATE #tables SET Processed = 1 where table_name = #table
END
SELECT * FROM Tables_to_Update where recordsToUpdate > 0
END
The following will find and replace a string in every database (excluding system databases) on every table on the instance you are connected to:
Simply change 'Search String' to whatever you seek and 'Replace String' with whatever you want to replace it with.
--Getting all the databases and making a cursor
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases
WHERE name NOT IN ('master','model','msdb','tempdb') -- exclude these databases
DECLARE #databaseName nvarchar(1000)
--opening the cursor to move over the databases in this instance
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #databaseName
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #databaseName
--Setting up temp table for the results of our search
DECLARE #Results TABLE(TableName nvarchar(370), RealColumnName nvarchar(370), ColumnName nvarchar(370), ColumnValue nvarchar(3630))
SET NOCOUNT ON
DECLARE #SearchStr nvarchar(100), #ReplaceStr nvarchar(100), #SearchStr2 nvarchar(110)
SET #SearchStr = 'Search String'
SET #ReplaceStr = 'Replace String'
SET #SearchStr2 = QUOTENAME('%' + #SearchStr + '%','''')
DECLARE #TableName nvarchar(256), #ColumnName nvarchar(128)
SET #TableName = ''
--Looping over all the tables in the database
WHILE #TableName IS NOT NULL
BEGIN
DECLARE #SQL nvarchar(2000)
SET #ColumnName = ''
DECLARE #result NVARCHAR(256)
SET #SQL = 'USE ' + #databaseName + '
SELECT #result = MIN(QUOTENAME(TABLE_SCHEMA) + ''.'' + QUOTENAME(TABLE_NAME))
FROM [' + #databaseName + '].INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = ''BASE TABLE'' AND TABLE_CATALOG = ''' + #databaseName + '''
AND QUOTENAME(TABLE_SCHEMA) + ''.'' + QUOTENAME(TABLE_NAME) > ''' + #TableName + '''
AND OBJECTPROPERTY(
OBJECT_ID(
QUOTENAME(TABLE_SCHEMA) + ''.'' + QUOTENAME(TABLE_NAME)
), ''IsMSShipped''
) = 0'
EXEC master..sp_executesql #SQL, N'#result nvarchar(256) out', #result out
SET #TableName = #result
PRINT #TableName
WHILE (#TableName IS NOT NULL) AND (#ColumnName IS NOT NULL)
BEGIN
DECLARE #ColumnResult NVARCHAR(256)
SET #SQL = '
SELECT #ColumnResult = MIN(QUOTENAME(COLUMN_NAME))
FROM [' + #databaseName + '].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = PARSENAME(''[' + #databaseName + '].' + #TableName + ''', 2)
AND TABLE_NAME = PARSENAME(''[' + #databaseName + '].' + #TableName + ''', 1)
AND DATA_TYPE IN (''char'', ''varchar'', ''nchar'', ''nvarchar'')
AND TABLE_CATALOG = ''' + #databaseName + '''
AND QUOTENAME(COLUMN_NAME) > ''' + #ColumnName + ''''
PRINT #SQL
EXEC master..sp_executesql #SQL, N'#ColumnResult nvarchar(256) out', #ColumnResult out
SET #ColumnName = #ColumnResult
PRINT #ColumnName
IF #ColumnName IS NOT NULL
BEGIN
INSERT INTO #Results
EXEC
(
'USE ' + #databaseName + '
SELECT ''' + #TableName + ''',''' + #ColumnName + ''',''' + #TableName + '.' + #ColumnName + ''', LEFT(' + #ColumnName + ', 3630)
FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE ' + #ColumnName + ' LIKE ' + #SearchStr2
)
END
END
END
--Declaring another temporary table
DECLARE #time_to_update TABLE(TableName nvarchar(370), RealColumnName nvarchar(370))
INSERT INTO #time_to_update
SELECT TableName, RealColumnName FROM #Results GROUP BY TableName, RealColumnName
DECLARE #MyCursor CURSOR;
BEGIN
DECLARE #t nvarchar(370)
DECLARE #c nvarchar(370)
--Looping over the search results
SET #MyCursor = CURSOR FOR
SELECT TableName, RealColumnName FROM #time_to_update GROUP BY TableName, RealColumnName
--Getting my variables from the first item
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #t, #c
WHILE ##FETCH_STATUS = 0
BEGIN
-- Updating the old values with the new value
DECLARE #sqlCommand varchar(1000)
SET #sqlCommand = '
USE ' + #databaseName + '
UPDATE [' + #databaseName + '].' + #t + ' SET ' + #c + ' = REPLACE(' + #c + ', ''' + #SearchStr + ''', ''' + #ReplaceStr + ''')
WHERE ' + #c + ' LIKE ''' + #SearchStr2 + ''''
PRINT #sqlCommand
BEGIN TRY
EXEC (#sqlCommand)
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH
--Getting next row values
FETCH NEXT FROM #MyCursor
INTO #t, #c
END;
CLOSE #MyCursor ;
DEALLOCATE #MyCursor;
END;
DELETE FROM #time_to_update
DELETE FROM #Results
FETCH NEXT FROM db_cursor INTO #databaseName
END
CLOSE db_cursor
DEALLOCATE db_cursor
Note: this isn't ideal, nor is it optimized
Here is another answer, similar to above (and hopefully more readable/efficient), since I recently had a similar requirement and this is how I solved it.
CREATE OR ALTER PROCEDURE UPDATE_ALL_COLUMNS
#TableNameSearchFilter NVARCHAR(100),
#TableSchema NVARCHAR(100),
#TestValue NVARCHAR(100),
#NewValue NVARCHAR(100)
AS
BEGIN
DECLARE #NRCOLUMNS INT;
DECLARE #i INT = 0;
DECLARE #COLUMN NVARCHAR(100) = '';
DECLARE #SQL NVARCHAR(MAX) = '';
DECLARE #TableToUpdate NVARCHAR(256) = '';
DECLARE #insertingNULL BIT;
IF (#NewValue IS NULL) SET #insertingNULL = 1
ELSE SET #insertingNULL = 0;
WHILE #TableToUpdate IS NOT NULL
BEGIN
SELECT #TableToUpdate = MIN(TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE #TableNameSearchFilter
AND TABLE_SCHEMA = #TableSchema
AND TABLE_NAME > #TableToUpdate;
WITH CTE1 AS
(
SELECT ROW_NUMBER() OVER (ORDER BY ORDINAL_POSITION) AS RN
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableToUpdate
AND TABLE_SCHEMA = #TableSchema
AND (#insertingNULL = 0 OR (#insertingNULL = 1 AND IS_NULLABLE = 'YES'))
)
SELECT #i = MIN(RN), #NRCOLUMNS = MAX(RN) FROM CTE1;
WHILE (#i <= #NRCOLUMNS AND #TableToUpdate IS NOT NULL)
BEGIN
WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY ORDINAL_POSITION) AS RN
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableToUpdate
AND TABLE_SCHEMA = #TableSchema
AND (#insertingNULL = 0 OR (#insertingNULL = 1 AND IS_NULLABLE = 'YES'))
)
SELECT #COLUMN = COLUMN_NAME
FROM CTE
WHERE RN = #i;
SET #SQL = #SQL +
N'UPDATE D SET ' + #COLUMN + N' = ' + ISNULL(N'''' + #NewValue + N'''', N'NULL')
+ N' FROM ' + #TableSchema + N'.' + #TableToUpdate + N' D WHERE CAST(D.' + #COLUMN + ' AS NVARCHAR) = ' + ISNULL(N'''' + #TestValue + N'''', N'NULL') + ';'
+ NCHAR(13) + NCHAR(10);
SET #i = #i + 1;
END;
END;
--PRINT SUBSTRING(#SQL, 1, 4000)
--PRINT SUBSTRING(#SQL, 4001, 8000)
--PRINT SUBSTRING(#SQL, 8001, 12000)
--PRINT SUBSTRING(#SQL, 12001, 16000)
--PRINT SUBSTRING(#SQL, 16001, 20000)
--PRINT SUBSTRING(#SQL, 20001, 24000)
EXEC (#SQL)
END
GO
As a usage example:
EXEC UPDATE_ALL_COLUMNS '%temp%', 'dbo', '', NULL
Parameters:
#TableNameSearchFilter - this will be used with the LIKE operator to find all the tables from your database whose names that match this value;
#TableSchema - the schema of the table (usually dbo)
#TestValue - the value to search for in ALL of the columns (and rows) of each found table;
#NewValue - the value to replace #TestValue with. Can also be NULL.
Explanation:
The EXEC statement will find ALL tables whose names contain the word 'temp', on the 'dbo' schema of your database, then search for the value '' (empty string) in ALL columns of ALL of the found tables, then replace this value with a NULL.
Obviously, if you have long(er) column/table names or the update value, make sure to update the limits on the parameters.
Make sure to first comment the last line (EXEC (#SQL)) and uncomment the lines with PRINT, just to get an idea for what the procedure does and how the final statements look like.
This is not going to work (most likely) if you want to search for the NULL value (i.e. to have #TestValue as NULL). Nevertheless, it can be easily changed to accomplish this as well, by replacing the equal sign from the WHERE clause (in the dynamic query) with IS NULL and removing the rest of the line, when #TestValue IS NULL.
Can be easily adapted to search for columns of only certain types (like VARCHAR etc).
The procedure accounts for inserting NULL values, and will only do so in NULLABLE columns.
Below is my SQL query that searches all columns in two tables (i didnt make this) i need to add this statement to it
, (tblUsers.Forename + ' ' + tblUsers.Surname) AS CleanName
but am unsure where to put it, can anyone help me out?
THanks
USE [ITAPP]
GO
/****** Object: StoredProcedure [dbo].[sp_SearchAllTables] Script Date: 07/11/2013 10:57:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[sp_SearchAllTables]
(
#SearchStr nvarchar(255)
)
AS
BEGIN
declare #where varchar(8000)
declare #sql varchar(8000)
set #sql = 'select * from tblUsers u join tblEquipment e on e.userid = u.id WHERE 1 = 1 AND ( 1= 0 '
select #where = coalesce(#where ,'' ) + ' OR ' + case when object_name(object_id) = 'tblUsers' then 'u' else 'e' end + '.[' + name + '] LIKE ''%' + replace(#SearchStr, '''', '''''') + '%'' '
from sys.columns where object_id in ( select object_id from sys.objects where name in ( 'tblUsers','tblEquipment' ))
and collation_name is not null
set #where = coalesce(#where, '') + ')'
print #sql
print #where
exec(#sql + #where)
END
change:
set #sql = 'select * from tblUsers u join tblEquipment e on e.userid = u.id WHERE 1 = 1 AND ( 1= 0 '
to
set #sql = 'select u.*,e.*, (u.Forename + '' '' + u.Surname) AS CleanName from tblUsers u join tblEquipment e on e.userid = u.id WHERE 1 = 1 AND ( 1= 0 '
Note: you need to delimit the single-quotes..
You're just adding to the select part of the statement on the set #sql line
here's it filled in.
USE [ITAPP]
GO
/****** Object: StoredProcedure [dbo].[sp_SearchAllTables] Script Date: 07/11/2013 10:57:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[sp_SearchAllTables]
(
#SearchStr nvarchar(255)
)
AS
BEGIN
declare #where varchar(8000)
declare #sql varchar(8000)
set #sql = 'select u.*, e.*, (tblUsers.Forename + '' '' + tblUsers.Surname) AS CleanName from tblUsers u join tblEquipment e on e.userid = u.id WHERE 1 = 1 AND ( 1= 0 '
select #where = coalesce(#where ,'' ) + ' OR ' + case when object_name(object_id) = 'tblUsers' then 'u' else 'e' end + '.[' + name + '] LIKE ''%' + replace(#SearchStr, '''', '''''') + '%'' '
from sys.columns where object_id in ( select object_id from sys.objects where name in ( 'tblUsers','tblEquipment' ))
and collation_name is not null
set #where = coalesce(#where, '') + ')'
print #sql
print #where
exec(#sql + #where)
END
Probably worth reading a SQL tutorial so you can see how this works:
Heres an example: http://www.w3schools.com/sql/sql_select.asp
We've just been given the following code as a solution for a complicated search query in a new application provided by offshore developers. I'm skeptical of the use of dynamic SQL because I could close the SQL statement using '; and then excute a nasty that will be performed on the database!
Any ideas on how to fix the injection attack?
ALTER procedure [dbo].[SearchVenues] --'','',10,1,1,''
#selectedFeature as varchar(MAX),
#searchStr as varchar(100),
#pageCount as int,
#startIndex as int,
#searchId as int,
#venueName as varchar(100),
#range int,
#latitude varchar(100),
#longitude varchar(100),
#showAll int,
#OrderBy varchar(50),
#SearchOrder varchar(10)
AS
DECLARE #sqlRowNum as varchar(max)
DECLARE #sqlRowNumWhere as varchar(max)
DECLARE #withFunction as varchar(max)
DECLARE #withFunction1 as varchar(max)
DECLARE #endIndex as int
SET #endIndex = #startIndex + #pageCount -1
SET #sqlRowNum = ' SELECT Row_Number() OVER (ORDER BY '
IF #OrderBy = 'Distance'
SET #sqlRowNum = #sqlRowNum + 'dbo.GeocodeDistanceMiles(Latitude,Longitude,' + #latitude + ',' + #longitude + ') ' +#SearchOrder
ELSE
SET #sqlRowNum = #sqlRowNum + #OrderBy + ' '+ #SearchOrder
SET #sqlRowNum = #sqlRowNum + ' ) AS RowNumber,ID,RecordId,EliteStatus,Name,Description,
Address,TotalReviews,AverageFacilityRating,AverageServiceRating,Address1,Address2,Address3,Address4,Address5,Address6,PhoneNumber,
visitCount,referalCount,requestCount,imgUrl,Latitude,Longitude,
Convert(decimal(10,2),dbo.GeocodeDistanceMiles(Latitude,Longitude,' + #latitude + ',' + #longitude + ')) as distance
FROM VenueAllData '
SET #sqlRowNumWhere = 'where Enabled=1 and EliteStatus <> 3 '
--PRINT('#sqlRowNum ='+#sqlRowNum)
IF #searchStr <> ''
BEGIN
IF (#searchId = 1) -- county search
BEGIN
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and Address5 like ''' + #searchStr + '%'''
END
ELSE IF(#searchId = 2 ) -- Town search
BEGIN
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and Address4 like ''' + #searchStr + '%'''
END
ELSE IF(#searchId = 3 ) -- postcode search
BEGIN
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and Address6 like ''' + #searchStr + '%'''
END
IF (#searchId = 4) -- Search By Name
BEGIN
IF #venueName <> ''
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and ( Name like ''%' + #venueName + '%'' OR Address like ''%'+ #venueName+'%'' ) '
ELSE
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and ( Name like ''%' + #searchStr + '%'' OR Address like ''%'+ #searchStr+'%'' ) '
END
END
IF #venueName <> '' AND #searchId <> 4
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and ( Name like ''%' + #venueName + '%'' OR Address like ''%'+ #venueName+'%'' ) '
set #sqlRowNum = #sqlRowNum + ' ' + #sqlRowNumWhere
--PRINT(#sqlRowNum)
IF #selectedFeature <> ''
BEGIN
DECLARE #val1 varchar (255)
Declare #SQLAttributes varchar(max)
Set #SQLAttributes = ''
Declare #tempAttribute varchar(max)
Declare #AttrId int
while (#selectedFeature <> '')
BEGIN
SET #AttrId = CAST(SUBSTRING(#selectedFeature,1,CHARINDEX(',',#selectedFeature)-1) AS Int)
Select #tempAttribute = ColumnName from Attribute where id = #AttrId
SET #selectedFeature = SUBSTRING(#selectedFeature,len(#AttrId)+2,len(#selectedFeature))
SET #SQLAttributes = #SQLAttributes + ' ' + #tempAttribute + ' = 1 And '
END
Set #SQLAttributes = SUBSTRING(#SQLAttributes,0,LEN(#SQLAttributes)-3)
set #sqlRowNum = #sqlRowNum + ' and ID in (Select VenueId from '
set #sqlRowNum = #sqlRowNum + ' CachedVenueAttributes WHERE ' + #SQLAttributes + ') '
END
IF #showAll <> 1
set #sqlRowNum = #sqlRowNum + ' and dbo.GeocodeDistanceMiles(Latitude,Longitude,' + #latitude + ',' + #longitude + ') <= ' + convert(varchar,#range )
set #withFunction = 'WITH LogEntries AS (' + #sqlRowNum + ')
SELECT * FROM LogEntries WHERE RowNumber between '+ Convert(varchar,#startIndex) +
' and ' + Convert(varchar,#endIndex) + ' ORDER BY ' + #OrderBy + ' ' + #SearchOrder
print(#withFunction)
exec(#withFunction)
As an aside, I would not use EXEC; rather I would use sp_executesql. See this superb article, The Curse and Blessings of Dynamic SQL, for the reason and other info on using dynamic sql.
See this answer.
Also, these:
Am I immune to SQL injections if I use stored procedures?
Avoiding SQL Injection in SQL query with Like Operator using parameters?
Can I protect against SQL Injection by escaping single-quote and surrounding user input with single-quotes?
Here's an optimized version of the query above that doesn't use dynamic SQL...
Declare #selectedFeature as varchar(MAX),
#searchStr as varchar(100),
#pageCount as int,
#startIndex as int,
#searchId as int,
#venueName as varchar(100),
#range int,
#latitude varchar(100),
#longitude varchar(100),
#showAll int,
#OrderBy varchar(50),
#SearchOrder varchar(10)
Set #startIndex = 1
Set #pageCount = 50
Set #searchStr = 'e'
Set #searchId = 4
Set #OrderBy = 'Address1'
Set #showAll = 1
--Select dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude)
DECLARE #endIndex int
SET #endIndex = #startIndex + #pageCount -1
;
WITH LogEntries as (
SELECT
Row_Number()
OVER (ORDER BY
CASE #OrderBy
WHEN 'Distance' THEN Cast(dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude) as varchar(10))
WHEN 'Name' THEN Name
WHEN 'Address1' THEN Address1
WHEN 'RecordId' THEN Cast(RecordId as varchar(10))
WHEN 'EliteStatus' THEN Cast(EliteStatus as varchar(10))
END) AS RowNumber,
RecordId,EliteStatus,Name,Description,
Address,TotalReviews,AverageFacilityRating,AverageServiceRating,Address1,Address2,Address3,Address4,Address5,Address6,PhoneNumber,
visitCount,referalCount,requestCount,imgUrl,Latitude,Longitude,
Convert(decimal(10,2),dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude)) as distance
FROM VenueAllData
where Enabled=1 and EliteStatus <> 3
And
(
(Address5 like #searchStr + '%' And #searchId = 1) OR
(Address4 like #searchStr + '%' And #searchId = 2) OR
(Address6 like #searchStr + '%' And #searchId = 3) OR
(
(
#searchId = 4 And
(Name like '%' + #venueName + '%' OR Address like '%'+ #searchStr+'%')
)
)
)
And
ID in (
Select VenueID
From CachedVenueAttributes
--Extra Where Clause for the processing of VenueAttributes using #selectedFeature
)
And
(
(#showAll = 1) Or
(#showAll <> 1 and dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude) <= convert(varchar,#range ))
)
)
SELECT * FROM LogEntries
WHERE RowNumber between #startIndex and #endIndex
ORDER BY CASE #OrderBy
WHEN 'Distance' THEN Cast(Distance as varchar(10))
WHEN 'Name' THEN Name
WHEN 'Address1' THEN Address1
WHEN 'RecordId' THEN Cast(RecordId as varchar(10))
WHEN 'EliteStatus' THEN Cast(EliteStatus as varchar(10))
END
The only thing I haven't fixed is the selection from CachedVenueAttributes that seems to build up a where statement in a loop. I think I might put this in a table valued function, and refactor it in isolation to the rest of the procedure.
I like dynamic SQL for search.
Where I have used it in the past I have used .Net prepared statements with any user generated string being passed in as a parameter NOT included as text in the SQL.
To run with the existing solution you can do a number of thing to mitigate risk.
White list input, validate input so that it can only contain a-zA-Z0-9\w (alpha numerics and white space) (bad if you need to support unicode chars)
Execute any dynamic sql as a restricted user. Set owner of stored proc to a user which has only read access to the tables concerned. deny write to all tables ect. Also when calling this stored proc you may need to do it with a user with similar restrictions on what they can do, as it appares MS-SQL executes dynamic sql within a storedproc as the calling user not the owner of the storedproc.
I've realized that this is a really old post, but when doing things like:
AND
(
(#showAll = 1)
OR (#showAll <> 1
AND dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude) <= convert(varchar,#range))
)
... an OPTION(RECOMPILE) will usually help pick a more concise plan, as long as it's not going to be executed a thousand times per second or anything.