Dynamically selecting and grouping by specified parameters in SQL Server - sql

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

Related

SQL Server foreign characters equal empty string

We have a query looking for empty values in a column, but it is returning results where the column is not empty.
As an example, this query returns true:
IF N'ការវិនិយោគបរទេស នៅជប៉ុន ធ្លាក់ចុះនៅឆ្នាំ២០១៧ នៅតែជាបញ្ហាធំបំផុត របស់ពិភពលោក' = N''
PRINT 'true'
ELSE
PRINT 'false';
I suspect it may be a collation issue, but I've tried forcing various collations and the result is still true.
IF N'ការវិនិយោគបរទេស នៅជប៉ុន ធ្លាក់ចុះនៅឆ្នាំ២០១៧ នៅតែជាបញ្ហាធំបំផុត របស់ពិភពលោក' = N'' COLLATE Arabic_CI_AI
PRINT 'true'
ELSE
PRINT 'false';
The text you want to compare seems to be Khmer, not Arabic, so you should use a Khmer_100_* collation.
Have a look at this TSQL snippet
declare #coll table (
name nvarchar(50),
khmerSupported bit
)
declare #text nvarchar(200) = N'ការវិនិយោគបរទេស នៅជប៉ុន ធ្លាក់ចុះនៅឆ្នាំ២០១៧ នៅតែជាបញ្ហាធំបំផុត របស់ពិភពលោក'
declare #name nvarchar(50), #sql nvarchar(500)
declare c cursor for
select name from sys.fn_helpcollations()
where name like 'khm%'
open c
fetch c into #name
while ##fetch_status = 0 begin
set #sql = 'select ''' + #name + ''', case when #text = N'''' COLLATE ' + #name + ' then 0 else 1 end'
print #sql
insert into #coll (name, khmerSupported)
exec sp_executesql #sql, N'#text NVARCHAR(200)', #text=#text
fetch c into #name
end
close c
deallocate c
select Name, KhmerSupported from #coll
and experiment with the collations query.
Use select Name, KhmerSupported from #coll where KhmerSupported=1 to find all collations supporting Khmer comparison.

procedure with multi where statement

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.

How to set dynamic query value to a variable in SQL Server stored procedure with parameters?

I want to set a variable to the value generated using the dynamic query outside the query.
I tried the sp_executesql concept, but this does not help me because I am using the parameter value in the dynamic query.
Are there any possibilities to fix this issue?
CREATE PROCEDURE [dbo].[SP_test_proc]
#id int = null,
#deptId int = null
As
BEGIN
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = '#id + case when #deptid is null then '' else #deptid End
SET #Query = 'Declare #Name varchar(100)
Set #Name = Select name from student '+ #Condition
SELECT *
FROM personal
WHERE name = #Name
END
Use output parameter in dynamic sql as shown here
Also see this
Try this :
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = '#id + case when #deptid is null then '' else #deptid End
Declare #Name varchar(100),#nameval varchar(100),#paramdef varchar(100)
SET #Query = '
Select #Name = name from student '+ #Condition
set #paramdef=N'#Name varhcar(20) OUTPUT'
execute sp_executesql #Query,#paramdef,#Name=#nameval Output
SELECT *
FROM personal
WHERE name = #nameval
Hope this code will work for you.
CREATE PROCEDURE [dbo].[SP_test_proc]
#id int = null,
#deptId int = null
As
BEGIN
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = ' + #id + ' And [Deptid] = ' + ISNULL(#deptid,'')
SET #Query = 'SELECT * FROM personal WHERE name IN ( SELECT name FROM student ' + #Condition + ')'
EXEC #Query
END
[Deptid] is not sure I don't know column name

Can I pass column name as input parameter in SQL stored Procedure

create procedure sp_First
#columnname varchar
AS
begin
select #columnname from Table_1
end
exec sp_First 'sname'
My requirement is to pass column names as input parameters.
I tried like that but it gave wrong output.
So Help me
You can do this in a couple of ways.
One, is to build up the query yourself and execute it.
SET #sql = 'SELECT ' + #columnName + ' FROM yourTable'
sp_executesql #sql
If you opt for that method, be very certain to santise your input. Even if you know your application will only give 'real' column names, what if some-one finds a crack in your security and is able to execute the SP directly? Then they can execute just about anything they like. With dynamic SQL, always, always, validate the parameters.
Alternatively, you can write a CASE statement...
SELECT
CASE #columnName
WHEN 'Col1' THEN Col1
WHEN 'Col2' THEN Col2
ELSE NULL
END as selectedColumn
FROM
yourTable
This is a bit more long winded, but a whole lot more secure.
No. That would just select the parameter value. You would need to use dynamic sql.
In your procedure you would have the following:
DECLARE #sql nvarchar(max) = 'SELECT ' + #columnname + ' FROM Table_1';
exec sp_executesql #sql, N''
Try using dynamic SQL:
create procedure sp_First #columnname varchar
AS
begin
declare #sql nvarchar(4000);
set #sql='select ['+#columnname+'] from Table_1';
exec sp_executesql #sql
end
go
exec sp_First 'sname'
go
This is not possible. Either use dynamic SQL (dangerous) or a gigantic case expression (slow).
Create PROCEDURE USP_S_NameAvilability
(#Value VARCHAR(50)=null,
#TableName VARCHAR(50)=null,
#ColumnName VARCHAR(50)=null)
AS
BEGIN
DECLARE #cmd AS NVARCHAR(max)
SET #Value = ''''+#Value+ ''''
SET #cmd = N'SELECT * FROM ' + #TableName + ' WHERE ' + #ColumnName + ' = ' + #Value
EXEC(#cmd)
END
As i have tried one the answer, it is getting executed successfully but while running its not giving correct output, the above works well
You can pass the column name but you cannot use it in a sql statemnt like
Select #Columnname From Table
One could build a dynamic sql string and execute it like EXEC (#SQL)
For more information see this answer on dynamic sql.
Dynamic SQL Pros and Cons
As mentioned by MatBailie
This is much more safe since it is not a dynamic query and ther are lesser chances of sql injection . I Added one situation where you even want the where clause to be dynamic . XX YY are Columns names
CREATE PROCEDURE [dbo].[DASH_getTP_under_TP]
(
#fromColumnName varchar(10) ,
#toColumnName varchar(10) ,
#ID varchar(10)
)
as
begin
-- this is the column required for where clause
declare #colname varchar(50)
set #colname=case #fromUserType
when 'XX' then 'XX'
when 'YY' then 'YY'
end
select SelectedColumnId from (
select
case #toColumnName
when 'XX' then tablename.XX
when 'YY' then tablename.YY
end as SelectedColumnId,
From tablename
where
(case #fromUserType
when 'XX' then XX
when 'YY' then YY
end)= ISNULL(#ID , #colname)
) as tbl1 group by SelectedColumnId
end
First Run;
CREATE PROCEDURE sp_First #columnname NVARCHAR(128)--128 = SQL Server Maximum Column Name Length
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT ' + #columnname + ' FROM Table_1'
EXEC(#query)
END
Second Run;
EXEC sp_First 'COLUMN_Name'
Please Try with this.
I hope it will work for you.
Create Procedure Test
(
#Table VARCHAR(500),
#Column VARCHAR(100),
#Value VARCHAR(300)
)
AS
BEGIN
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #Table + ' WHERE ' + #Column + ' = ' + #Value
--SELECT #sql
exec (#sql)
END
-----execution----
/** Exec Test Products,IsDeposit,1 **/

Execute a Stored Procedure in a SELECT statement

For an instance I a select statement and it is returning 1000 rows. I need to execute a particular stored procedure for every row the the select statement is returning.
have you got any idea how can I do that?
Construct the EXECUTE statements in your select like this:
SELECT 'EXEC sp_whatever ' + parameter stuff
FROM your_table
Then run the results! Alternatively, paste your results into a spreadsheet package, and use string concatenation to construct the EXEC statements - just create a formula and paste it down the 1,000 rows. I personally prefer the first approach.
To clarify the "parameter stuff", take the example of a stored procedure that takes two int parameters that you want to take from columns you your_table. You'd then have something like this:
SELECT 'EXEC sp_whatever ' + CAST(field1 AS varchar) + ', ' + CAST(field2 AS varchar)
FROM your_table
Not the need to be careful with string fields here - you run the risk of inadvertently exposing yourself to your own SQL injection attack, as with any SQL string concatenation.
I am reading your "for an instance" as "this is a one-off task". If this is a task that needs automating, then one of the other answers may be the right approach.
You can do it like this:
declare #execstatementsbatch nvarchar(max)
select #execstatementsbatch = ''
SELECT #execstatementsbatch = #execstatementsbatch + 'EXEC UpdateQty ' + ItemCode + ', ' + QtyBO + '; '
FROM ITEMSPO
INNER JOIN .....
<some conditions>
exec(#execstatementsbatch)
Disclaimer: I'm not sure if I understand your question correctly.
Assuming you are on SQL Server 2005 upwards, you could create a table-valued user defined function and use the OUTER APPLY operator in your query.
Most RDBMS will let you select rows from stored procedure result sets. Just put your stored procedures in the FROM clause, as you would for common table expressions. For instance:
SELECT sp.ColumnInResultSet, t.BaseTableColumnName
FROM sp_whatever ( Args) sp INNER JOIN BaseTable t ON t.ID = sp.ID;
CREATE PROCEDURE dbo.usp_userwise_columns_value
(
#userid BIGINT
)
AS
BEGIN
DECLARE #maincmd NVARCHAR(max);
DECLARE #columnlist NVARCHAR(max);
DECLARE #columnname VARCHAR(150);
DECLARE #nickname VARCHAR(50);
SET #maincmd = '';
SET #columnname = '';
SET #columnlist = '';
SET #nickname = '';
DECLARE CUR_COLUMNLIST CURSOR FAST_FORWARD
FOR
SELECT columnname , nickname
FROM dbo.v_userwise_columns
WHERE userid = #userid
OPEN CUR_COLUMNLIST
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
FETCH NEXT FROM CUR_COLUMNLIST
INTO #columnname, #nickname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #columnlist = #columnlist + #columnname + ','
FETCH NEXT FROM CUR_COLUMNLIST
INTO #columnname, #nickname
END
CLOSE CUR_COLUMNLIST
DEALLOCATE CUR_COLUMNLIST
IF NOT EXISTS (SELECT * FROM sys.views WHERE name = 'v_userwise_columns_value')
BEGIN
SET #maincmd = 'CREATE VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), #userid) + ') as userid , '
+ CHAR(39) + #nickname + CHAR(39) + ' as nickname, '
+ #columnlist + ' compcode FROM dbo.SJOTran '
END
ELSE
BEGIN
SET #maincmd = 'ALTER VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), #userid) + ') as userid , '
+ CHAR(39) + #nickname + CHAR(39) + ' as nickname, '
+ #columnlist + ' compcode FROM dbo.SJOTran '
END
--PRINT #maincmd
EXECUTE sp_executesql #maincmd
END
-----------------------------------------------
SELECT * FROM dbo.v_userwise_columns_value