Get multiple variables from table and store them in some variables - sql

For this example, the query and result are:
Query:
select name from sys.parameters where object_id = ##PROCID;
Result:
name
#BKP_TBL
#Filter
I want to store the actual value of #BKP_TBL and #Filter in another variable. Therefore, I am using string_agg to aggregate the result into a single row and store it in new variable as shown below:
DECLARE #PARAMS_DER VARCHAR(MAX);
select #PARAMS_DER = string_agg(name,'+') from sys.parameters where object_id = ##PROCID group by object_id;
The problem is that #PARAMS_DER is storing #BKP_TBL+#Filter instead of their values.

Related

Anything like template literals while writing a search query in SQL?

I am writing a stored procedure to get a particular value.
declare #num int
set #num = (SELECT Id
FROM [sometable]
WHERE Name like '%today%')
-- returns #num = 1
Select Value
FROM [anothertable]
where name like 'days1'
In the last line of the query I want to add "1" or any other number after 'days', depending on the variable #num.
How can I do it, sort of like how we use template literals in Javascript, using the ${} syntax but in SQL?
You can just use the first query as a sub-query of the second:
select [Value]
from anothertable
where [name] = Concat('days', (select Id from sometable where [Name] like '%today%'));

why I need a dynamic query for "column value from a query"

For example this returns a value from a query, which I will then use as a column name.
#A=Select top 1 productid from productlist order by timestamp desc
then I would like this "productid" A to be used in the other table
Select #A from customerlist
then the result is #A value instead of field value in customerlist.
When I use dynamic query, I can get right result.
Why?
(I know I can use join but because this productlist table is dynamic, so let's assume it is a sub query)
You need "dynamic SQL" because SQL will NOT allow you to use a parameter as a column name or a table name. You can only use parameters for data values such as in a where clause where column1 = #val
set #A = 'çolumn1'
Select #A from customerlist -- this fails because it is not allowed
Dynamic SQL is a "hack" to get around those restrictions as the SQL statement is placed into a string along with any value held by parameters.
set #A = 'çolumn1'
set #SQL = 'Select ' + #A + ' from customerlist;'
execute #SQL -- this works, the SQL statement is valid with no parameters as column names
The string formed as #SQL is a complete sql statement without needing any parameters as column names.
Note: the syntax I used here is incomplete and is based on MS SQL Server, different databases will use a different, but similar, syntax.

SQL Server: set a variable with more than one possible value

I need your help. I have a command to update rows, this works if in a variable #ID contain one value.
DECLARE #ID nvarchar(100)
SET #ID = (select top 1 id from [SERVICES])
DECLARE #UPDATE nvarchar(max)
SET #UPDATE ='UPDATE SERVICES
SET SERVICES.options = t1.options
FROM SERVICES t
JOIN (SELECT *
FROM OPENQUERY([ORI], ''SELECT ID, options
FROM log
WHERE ID = ''''' + #ID + ''''' '')) t1 ON t1.id = t.id'
EXEC (#UPDATE)
but I need to update more than 1 rows.
If I specify a condition like this:
SET #ID = (SELECT id FROM [ReportSM].[dbo].[SERVICES])
I get an error:
Subquery returned more than 1 value.
How to fix it?
It sounds like you really want to pass a table valued parameter into the open query, but that's not supported. You can remove that filter and let the join take care of the update accuracy but that will result in a potentially much more expensive remote query than necessary. That solution would just look like this:
UPDATE
t
SET
t.options = t1.options
FROM
Services t
JOIN (SELECT ID, options FROM OPENQUERY([ORI], 'SELECT ID, options FROM
log')) t1 ON t1.id = t.id
However, if you have control over the ORI linked server, you could set up a linked server there back to your ReportSM server. That would let you create a view on your ORI server that contains all of the IDs from your [ReportSM].[dbo].[SERVICES] table which is what you are trying to filter your log table on. That means you could perform the ID filtering on the ORI side and then run a simpler update on the ReportSM side. Something like this on the ORI side:
CREATE VIEW vReportServiceIDs
AS
SELECT
ID
FROM
[ReportSM].[dbo].[SERVICES]
CREATE VIEW vReportServiceLogs
AS
SELECT
reportService.ID,
oriLog.options
FROM
vReportServiceIDs reportService
JOIN [log] oriLog ON reportService.ID = [log].ID
And then on your ReportSM side:
UPDATE
t
SET
t.options = t1.options
FROM
SERVICES t
JOIN (
SELECT
ID, options
FROM
OPENQUERY([ORI], 'SELECT ID, options FROM vReportServiceLogs')
If you do not have that kind of access to the ORI server and the logs table has too much data for you to just query it all and exclude what you don't need during the join, you might want to consider creating a cache of the logs table that you update from a job on the ReportSM server and then just joining on that during your update.
Option 1
In your current setup, you could pass #ID as a CSV to OPENQUERY such that WHERE IN would work.
WHERE ID = ' + #ID + '
could then be replaced with
WHERE ID IN (' + #IDs + ')'
Look here to convert your ID column into a CSV: SQL Server convert select a column and convert it to a string
Be aware of the limit on the length of the IN clause. See this https://dba.stackexchange.com/questions/14161/what-is-the-maximum-number-of-parameters-i-can-pass-using-the-sql-in-clause-in-s
Option 2
Since concatenating data directly into a query has SQL injection concerns, you could also look at a more structured approach of using FOR XML to convert the IDs into an xml fragment and passing that into OPENQUERY and within that reading the ids out using OPENXML.
If both your servers are SQL Server 2016 or above, you could also use JSON as your format for transferring the ids instead of XML. You would use FOR JSON to create a JSON array containing the ids and use OPENJSON on the destination SQL server to convert the JSON back into a rowset that you can join.
declare #json varchar(max) = (select id from [ReportSM].[dbo].[SERVICES] FOR JSON PATH)
This will generate a string like this
[{"id":1},{"id":2},{"id":3},{"id":4}]
You can add this into a variable in the query you are preparing and read it using below
SELECT ID
FROM OPENJSON (#json, '$')
WITH (ID IN '$.id')
Putting it together your query would look like this:
declare #json varchar(max) = (select id from [ReportSM].[dbo].[SERVICES] FOR JSON PATH)
DECLARE #UPDATE nvarchar(max)
SET #UPDATE ='UPDATE SERVICES
SET SERVICES.options = t1.options
FROM SERVICES t
JOIN (SELECT *
FROM OPENQUERY([ORI], ''DECLARE #json nvarchar(max) = ''''' + #json + '''''
SELECT ID, options
FROM log
WHERE ID IN (SELECT ID FROM OPENJSON (#json, ''$'') WITH (ID IN ''$.id'')))) t1 ON t1.id = t.id'
EXEC (#UPDATE)

Using column variables in my WHERE clause

I have two tables (tableA and tableB) both with a name column. tableA's name column might be called NAME, tableB's column might be called FULLNAME, but they both are supposed to have the same value.
I am to write a query that pulls member id's (from either table) where these two column values are not the same. However, I'd like to pass the column names I'm checking via parameter, as this will be going in an SSRS report and in the future i'd like to be able to use it to compare any other column between these two tables.
Something like this:
DECLARE #COLUMN_A VARCHAR(50), #COLUMN_B VARCHAR(50)
/* COLUMN PARAMS WILL BE PASSED IN VIA SSRS */
SELECT
DISTINCT(MEMBER_ID)
FROM
TABLE_A
JOIN TABLE_B
ON (TABLE_A.MEMBER_ID = TABLE_B.MEMBER_ID)
WHERE
#COLUMN_A <> #COLUMN_B
Is something like this possible?
edit:
Or might something like this work?
DECLARE
#column VARCHAR(50)
SELECT #column = 'FIRST_NAME';
SELECT DISTINCT
MEMBR_ID,
case
when #column='FIRST_NAME' then MEMBR_FIRST_NAME
when #column='LAST_NAME' then MEMBR_LAST_NAME
end TABLE_1,
case
when #column='FIRST_NAME' then FIRSTNAME
when #column='LAST_NAME' then LASTNAME
end TABLE_2,
#column
FROM
TABLE_1
JOIN TABLE_2
ON (TABLE_1.MEMBR_ID = TABLE_2.MEMBR_ID)
WHERE
TABLE_1.#column <> TABLE_2.#column
Is something like this possible?
Technically, the syntax is fine. The where will be comparing two constant strings. The results will be either all rows or no rows, depending on whether the two strings are the same.
Do these evaluate to the columns? No, they do not. You cannot pass parameters into a SQL statement for identifiers -- column names, table names, schema names, database names, function names, or operators (for example).
You can do this using dynamic SQL, but you have to plug the names in:
DECLARE #sql NVARCHAR(MAX);
SET #sql = '
SELECT DISTINCT A.MEMBER_ID
FROM TABLE_A A JOIN
TABLE_B B
ON A.MEMBER_ID = B.MEMBER_ID
WHERE A.#COLUMN_A <> B.#COLUMN_B
';
SET #sql = REPLACE(#sql '#COLUMN_A', COLUMN_A);
SET #sql = REPLACE(#sql '#COLUMN_B', COLUMN_B);
exec sp_executesql #sql;

How to write an attribute name to the select query dynamically

I have a table including:
ID Name Period0Id Period1Id Period2Id
What I would like to receive data based on a user-defined parameter #check.
Lets assume:
declare #check int = 1;
In this case I need to get Period1Id value from the table. So I need to have something like that:
Select ID, Name, StatusId = Period + #check + Id -- #check is the parameter
From mytable
However, my query is not working. How can I fix this?
Your table looks like it is not in first normal form.
Instead of three columns for Period0Id to Period2Id you could have a column for PeriodIndex with values of (0,1,2) and a single column for PeriodId and then it would be just a WHERE PeriodIndex = #Check
You can't select a column using string interpolation with a variable as you are attempting. You can use dynamic SQL to create the SQL String dynamically. Or simply hardcode the options if they all have the same dataype.
Select ID,
Name,
StatusId = CASE #Check WHEN 0 THEN Period0Id
WHEN 1 THEN Period1Id
WHEN 2 THEN Period2Id
END
From mytable
Here is an alternative way that will create dynamic columns, which is essentially using your original query:
DECLARE #check VARCHAR = 1
DECLARE #sqlquery NVARCHAR(MAX)
SET #sqlquery = N'SELECT ID, Name, StatusId = Period'+#check+'Id
FROM mytable'
EXEC sp_executesql #sqlquery