using parameters in group by statment - sql-server

I just wanna do a simple query with grouping by "Age_Band".
And to set the parameter to help me change the grouping column easily.
But got an error:
"Each GROUP BY expression must contain at least one column that is not an outer reference. "
How did I make mistake on using the parameter? Thanks a lot
DECLARE #group nvarchar(50);
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
SET #SQLString =
N'SELECT AVG(Loan_Amount) as Avg_Loan_Amount
,count(Loan_Amount)
,SUM(Loan_Amount) as Sum_Loan_Amount
,SQRT(SUM(Loan_Amount)) as Square_Loan_Amount
,Age_Band
,SUM(Interest_Paid_Amount) as Sum_Interest_Paid_Amount
,GETDATE()as today
FROM [dbo].[Loan]
group by #groupcol';
SET #ParmDefinition = N'#groupcol nvarchar(50)';
SET #group=N'Age_Band'
exec sp_executesql #SQLString, #ParmDefinition,
#groupcol = #group;

You cannot use parameter in the group by clause by you can build query string using parameter. Something like this.
DECLARE #group nvarchar(50) = N'Age_Band'; --set col name
DECLARE #SQLString nvarchar(500);
SET #SQLString =
N'SELECT AVG(Loan_Amount) as Avg_Loan_Amount
,count(Loan_Amount)
,SUM(Loan_Amount) as Sum_Loan_Amount
,SQRT(SUM(Loan_Amount)) as Square_Loan_Amount
,' + #group + '
,SUM(Interest_Paid_Amount) as Sum_Interest_Paid_Amount
,GETDATE()as today
FROM [dbo].[Loan]
group by ' + #group;
exec sp_executesql #SQLString;

Related

To add a local variable value to a dynamic query result set

I have a dynamic query where in I want to add the value of a local variable to every row of the result set. I have a simplified version of the example below.
While this query works fine:
DECLARE #purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
SELECT Name, #purchaseDate
FROM Fruits
The similar one in dynamic SQL does not work:
DECLARE #query AS NVARCHAR(MAX),
#purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
SET #query = 'SELECT Name, #purchaseDate FROM Fruits'
EXEC sp_executesql #query
I get the error
Must declare the scalar variable "#purchaseDate".
So I assumed I might need to declare my purchaseDate inside the query as the dynamic SQL query cannot access the variable. So I tried this:
DECLARE #query AS NVARCHAR(MAX)
SET #query = 'DECLARE #purchaseDate AS DATE' +
'SET #purchaseDate = ' + '12/23/2020 ' +
'SELECT Name, #purchaseDate FROM Fruits'
EXEC sp_executesql #query
But I get the same error message.
How do I go about fixing it?
You can't reference a variable declared outside of a dynamic statement inside a dynamic statement. You need to add the declaration in the parameters and pass the value:
DECLARE #query AS nvarchar(MAX),
#purchaseDate AS date;
SET #purchaseDate = '20201223';
SET #query = 'SELECT Name,#purchaseDate FROM Fruits;';
EXEC sp_executesql #query, N'#purchaseDate date', #purchaseDate = #purchaseDate;
The recommended way to put a variable in dynamic query with a prepared statement:
if OBJECT_ID('Fruits') is not null drop table Fruits
create table Fruits(
name varchar(100)
)
insert into Fruits
values('apple')
DECLARE #purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
DECLARE #P1 int;
EXEC sp_prepare #P1 output,
N'#purchaseDate date',
N'SELECT Name, #purchaseDate FROM Fruits';
EXEC sp_execute #P1, #purchaseDate;
EXEC sp_unprepare #P1;
Take a look at the Microsoft Doku.
If you are lazy or just want a simple adhoc solution (not recommended):
if OBJECT_ID('Fruits') is not null drop table Fruits
create table Fruits(
name varchar(100)
)
insert into Fruits
values('apple')
DECLARE #query AS NVARCHAR(MAX),
#purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
set #query = 'SELECT Name, ' + convert(nvarchar(10),#purchaseDate, 12) + ' FROM Fruits'
exec sp_executesql #query

Dynamic Column Name in SQL in Update statement

DECLARE #sql NVARCHAR(max)
DECLARE #ParmDefinition NVARCHAR(500)
SET #sql = 'UPDATE [Table1] SET [Table1].[#columnName] = TEST';
SET #ParmDefinition = N'#columnName NVARCHAR(50)';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'MyColumn';
When I run the above query, I get Invalid column name '#columnName'.. Clearly, the column name is not being replaced when the query is run.
In reality, my #sql variable is much larger and I have many columns I wish to update, thus I would like to avoid doing SET SQL = for all enumerations of the column name.
I'd like to declare the sql string once, and invoke the query with different values. e.g.:
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'MyColumn';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'AnotherColumn';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'YetAnotherColumn';
-- And so on
Is something like this possible?
Yes, you have to concatenate the variable outside the string. In other words:
SET #sql = 'UPDATE [Table1] SET [Table1].[' + #columnName + '] = t1.Value ' +
EDIT: Another solution we have used is to replace tokens in the base sql to construct a new sql variable for execution.
DECLARE #sql nvarchar(max) = 'SELECT #ColumnName FROM #TableName';
DECLARE #sql2 nvarchar(max) = REPLACE(REPLACE(#sql,'#ColumnName',#ColumnNameVariable),'#TableName',#TableNameVariable)
EXEC (#sql2)
...Some code that changes the values of #ColumnNameVariable and #TableNameVariable...
DECLARE #sql2 nvarchar(max) = REPLACE(REPLACE(#sql,'#ColumnName',#ColumnNameVariable),'#TableName',#TableNameVariable)
EXEC (#sql2)
And you'll notice that the Declaration and Exec of SQL2 are exactly the same lines in both cases. This lends itself to use in a LOOP if that is applicable. (Except that you wouldn't DECLARE #Sql2 in the loop...just populate/re-populate it).
First of all, kudos for trying to parameterize your dsql using sp_executesql. The problem is, you can only parameterize something you could put into a variable in the first place such as in a search predicate or select list.
However it's still possible; just concatenate the column name with your DSQL string, wrapping it with the quotename function
set #sql = 'update table1 set table1.' + quotename(#ColumnName) + ' = ...

Use sql variable as where clause in sql

i want to do this:
DECLARE #str varchar(max)
DECLARE #cnt bigint
set #str= 'where column=value'
set #cnt= (select count(*) from user+#str)
but the where clause is not working. getting no error but it will just ignore the where condition.
I previously suggested wrapping your last line in EXEC(), but you can't do this and return results to a variable. To do that, use the following in place of your last line:
create table #temp (theCount int)
insert into #temp EXEC('select count(*) from Photos '+#str)
set #cnt= (select top 1 theCount from #temp)
drop table #temp
Check below code, in your case condition is dynamic so you need to dynamic sql.
DECLARE #sSQL nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #str nvarchar(500);
set #str= 'where column=value'
SELECT #sSQL = N'SELECT #retvalOUT = COUNT(*) FROM user '+ #str +' ' ;
SET #ParmDefinition = N'#retvalOUT int OUTPUT';
EXEC sp_executesql #sSQL, #ParmDefinition, #retvalOUT=#retval OUTPUT;
SELECT #retval;
use the execute sql syntax http://technet.microsoft.com/en-us/library/ms188001.aspx
Hope this will solve your problem

Can I specify a database name dynamically in a trigger?

I need to get data from a table in a database who's database name will be determined as a variable during a trigger. I then, knowing this variable need to get a seqno from a table in the determined database for a item which was also determined as a variable during a trigger.
I am trying this route as I assume I need to build the SQL statement before I set it to a variable.
This is not working and I need to know the best way on how I can do this:
DECLARE #SU_SEQNO INTEGER, #SU_NAME VARCHAR(50), #SU_OWNER VARCHAR(15), #SUD_SEQNO INTEGER, #SQL NVARCHAR(500)
SET #SU_OWNER = 'XXX'
SET #SU_NAME = '1ABC234'
SET #SQL ='SELECT #SUD_SEQNO=SEQNO FROM ' + (#SU_OWNER) + '.SU_MAIN
WHERE UNITNAME= ' + #SU_NAME
SET #SUD_SEQNO = (EXECUTE (#SQL))
Thanks alot for any help with this
From: Get result from dynamic SQL in stored procedure
SET #SQL = N'SELECT DISTINCT #FiscalYear = FiscalYear FROM ' + #DataSource;
EXEC sp_executesql #SQL, N'#FiscalYear INT OUTPUT', #FiscalYear OUTPUT;
PRINT #FiscalYear;
I'd re-engineer to use the sp_executesql method as shown above. That should do the trick.
I have amended the code, and it works
declare #su_owner varchar(15) = 'DBTEST'
declare #SU_SEQNO INTEGER=1, #SUD_SEQNO INTEGER=0, #SQL NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500), #SU_NAME_INPUT VARCHAR(50)='SU123'
SET #SU_NAME_INPUT = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SU_NAME = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SQL = N'SELECT #sud_seqnoOUT=MAX(SEQNO) FROM ' + quotename(#su_owner) + '.[dbo].[SU_MAIN] WHERE UNITNAME]=#SU_NAME_INPUT' ;
SET #ParmDefinition = N'#SU_NAME_INPUT VARCHAR(50),#sud_seqnoOUT INT OUTPUT'
EXEC sp_executesql #SQL,#ParmDefinition,#SU_NAME_INPUT = #SU_NAME,
#sud_seqnoOUT = #SUD_SEQNO OUTPUT

SQL Server variable columns name?

I am wondering why I cannot use variable column name like that:
declare #a as varchar;
set #a='TEST'
select #a from x;
Thank you
You can't do it because SQL is compiled before it knows what the value of #a is (I'm assuming in reality you would want #a to be some parameter and not hard coded like in your example).
Instead you can do this:
declare #a as varchar;
set #a='TEST'
declare #sql nvarchar(max)
set #sql = 'select [' + replace(#a, '''', '''''') + '] from x'
exec sp_executesql #sql
But be careful, this is a security vulnerability (sql-injection attacks) so shouldn't be done if you can't trust or well clean #a.
Because it is not allowed.
Insted of this you could use dynamic sql query:
declare #a as varchar;
set #a='TEST'
exec ('select ' + #a + ' from x')
Because the column names are resolved at compile time not at run time for the SQL statement.
use sp_executesql for this
Example
SET #SQLString = N'SELECT *
FROM table1
WHERE timet = #time and items in (#item)';
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
SET #ParmDefinition = N'#time timestamp,
#item varchar(max) ';
EXECUTE sp_executesql
#SQLString
,#ParmDefinition
,#time = '2010-04-26 17:15:05.667'
,#item = '''Item1'',''Item2'',''Item3'',''Item4'''
;
If there are not too many columns to chose, how about a CASE WHEN statement?
DECLARE #ColName VARCHAR(50) = 'Test1'
SELECT [Foo], [Bar],
CASE
WHEN #ColName = 'Test1' THEN [Test1]
WHEN #ColName = 'Test2' THEN [Test2]
WHEN #ColName = 'Test3' THEN [Test3]
WHEN #ColName = 'Test4' THEN [Test4]
ELSE [TestDefault]
END [TestResult]
FROM [TableName];
This avoids using any EXEC.

Resources