Manipulate a string to be used in IN operator (SQL, SSRS) - sql-server

I have an SSRS report with dataset that has query looking like this (simplified as the query is a lot more complex that this):
select *
from myTable
where country in (#Country)
#Country is a parameter and can have multiple values
It works fine, but we were asked to roll up all the values from one country to another one (the country is not in the list of parameters to be selected).
So for example we need to roll up all the records that belong to Canada to US if US was selected as one of the Countries essentially just replacing 'Canada' with 'US' in the dataset returned which is easy to achieve using REPLACE or even CASE statement. However, it gets a bit tricky with WHERE clause in the query. Does anyone know how to replace a string with another string so it's understood by IN operator?
If I simply do something like this:
select *
from myTable
where country in (replace(#Country, 'US', 'US,Canada'))
the query doesn't return anything and I understand the reasons behind it.
If I test this hardcoding the values like this:
select *
from myTable
where country in ('US', 'Canada')
it returns the correct rows of data. I think I'm looking for a way to join multiple values so it's understood properly by IN operator.
TIA,
-TS.

You can use Dynamic SQL to achieve this. All you need to do is pass country name in declaration at first line.
Look at the code below:
DECLARE #SQL NVARCHAR(2000), #Country VARCHAR(30) = 'Canada'
SELECT #SQL = CONCAT('
select
*
from myTable
where country in (',
CASE WHEN #Country = 'US' THEN '''US'', ''Canada''' else '#Country' end , ')'
)
EXEC sp_executesql #SQL, N'#Country VARCHAR(30)', #Country = #Country
Here's a fiddle : http://sqlfiddle.com/#!18/b7f49/4

Possibly the same answer as SQL in (#Variable) query
You may need to add REPLACE to that function based on your requirements.

Related

Retrieving column in deleted/inserted tables referencing another table

It may save some time to skip the "why" on this.
I would like to compile a string within a SQL Server trigger that contains all the values in the inserted or deleted tables, including their column names. I'm not used to doing something this extensive in SQL so I may be approaching this entirely wrong.
Something like:
CREATE OR ALTER TRIGGER [dbo].[TRIGGER_NAME]
ON [MY_TABLE_NAME]
AFTER UPDATE, INSERT
AS
DECLARE #columns TABLE(COLUMN_NAME VARCHAR(100))
INSERT INTO #columns
SELECT
COLUMN_NAME, DATA_TYPE
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'MY_TABLE_NAME'
DECLARE #string nvarchar(MAX)
SET #string = (SELECT TOP 1
STRING_AGG(CONCAT('s:', LEN(COLUMN_NAME), ':"',
COLUMN_NAME, '";', inserted.COLUMN_NAME), ';' )
FROM #columns)
This doesn't tell the full story, but the critical piece is the inserted.COLUMN_NAME part. The value in COLUMN_NAME is a varchar and I want that value to be evaluated as the name of the column in the inserted table.
I explored writing a function or stored procedure but fear I may be overthinking this. It feels like a basic thing to do. In javascript I'd simply say something like inserted[Column_Name] but SQL is a whole different beast...
--EDIT--
What I want is to be able to evaluate the value held in a variable as a column name. i.e.
DECLARE #var varchar(50)
SET $var = 'name';
SELECT TABLE_NAME.$var (should get the column named 'name' from table named TABLE_NAME)
Ultimate goal is to...
Say there is a table like this:
Row NUM
Email
Name
1
Jack#name.com
Jack
2
Jill#name.com
Jill
I want to be able to be able convert this table into a single string like this without knowing all the names of the columns ahead of time.
'Email:Jack#name.com,Name:Jack;Email:Jill#name.com,Name:Jill;'

If clause in sql

Declare #a varchar(100);
If #a = select productname from product
The above mentioned query is throwing error as my sql query (select product name from product) is returning multiple values.
Can someone help me with this?
Try something like:
Declare #a varchar(100);
SELECT #a = 'MyProduct'
Then either
If #a = select TOP 1 productname from product ORDER BY <some field>
OR
If #a IN (select productname from product)
However, how do you know which product(s) to match to; you might need a WHERE clause. Some sample data and desired results would help.
Please note that you need to put the SELECT query inside the parenthesis in this case! Also you should note that your select query, in this case should not return more than one value. So to resolve these you need to write it as:
Declare #a varchar(100);
If #a = (select TOP 1 productname from product)
However your query logically seems to be invalid and you should rethink about it, for example you need to say that, you are going to check #a with which product? You might need to add some filters to query and/or add ELSE to your if, etc.
You might also need to read the #PeterSmith's answer too(Using IN...)

How to search by multiple values in "Always Encrypted" column?

If I am using deterministic encryption I am able to perform searches in encrypted column. For example:
DECLARE #email NVARCHAR(222) = 'test';
SELECT *
FROM Users
Where email = #email;
But how to perform searches by multiple values? For example, if I want to get records where email addresses are userA#gmail.com, userB#gmail.com and userC#gmail.com.
I can't create many input parameters because usually the values are dynamic and passed as CSV string, for example.
In you situation, I would recommend splitting if it suits you.
Try this query: Let me know if it works for you
DECLARE #email NVARCHAR(222) = 'userA#gmail.com,userB#gmail.com,userC#gmail.com'
DECLARE #Delimiter NCHAR(1) = ','
DECLARE #EmailParam TABLE(email NVARCHAR(222))
;WITH Split(StPos,EndPos)
AS(
SELECT 0 AS StPos, CHARINDEX(#Delimiter,#email) AS EndPos
UNION ALL
SELECT EndPos+1, CHARINDEX(#Delimiter,#email,EndPos+1)
FROM Split
WHERE EndPos > 0
)
INSERT INTO #EmailParam (email)
SELECT SUBSTRING(#email,StPos,COALESCE(NULLIF(EndPos,0),LEN(#email)+1)-StPos) FROM Split
SELECT Users.*
FROM Users
INNER JOIN #EmailParam EmailParam
ON Users.email = EmailParam.email;
Explanation:
I have just extended your query to deal with multiple emails.
As you mentioned if are sure about input format being comma separated string, it might worth trying to derive table variable from it.
So If your input is like:
DECLARE #email NVARCHAR(222) = 'userA#gmail.com,userB#gmail.com,userC#gmail.com'
You can split it using any tsql technique, I have used common table expression here.
Then once you have it in tabular format. I have got it in table variable named #EmailParam here. You can then easily use add a join and filter it.
if your query works with deterministic encryption, then this query may work in the similar way as only additional change in regard to filer is we have inner join now. ,

Dynamic table in SQL Server

I have a really weird and complex requirement that I need help with. I have a table let's say Tasks that contains all the tasks for a user/system. I need to filter out the tasks per user and show it in UI. But here is the scene, the Tasks table contains a column base_table that stores the table name (real SQL Server table) on which it is based. It also stores the base table id which navigates to a particular record in the base table. Now I need to add some filter in the base table and if it satisfies the task would get retrieved.
I did try to put up a procedure which would hit a select query against base table and also check conditions.
CREATE PROCEDURE gautam_dtTable_test
(#TableName AS nvarchar(max))
AS
BEGIN try
declare #sql nvarchar(max)
declare #ret tinyint
set #ret = 0
set #sql = 'select 1 where exists (Select top 1 Id from ' + #TableName+' where some_condition)';
Exec sp_executesql #sql, N'#var tinyint out', #ret out
return #ret
end try
begin catch
return 0
end catch
I have used the procedure to input table name and hit some conditions and return a flag, 1/0 kind of thing. I also want to use try catch so that if there is any error, it would return false.
That's why I have used the procedure, not function. But seems like we can use this procedure into sql statement. Overall what I have in my mind is
Select *
from tasks
where some_conditions
and procedure/function_to_check(tasks.base_table)
Key issues with my approach
The base_table name could be invalid, so are some columns in it. So, I would love to use a try-catch.
Need to Embed it as sub-query to avoid parallel operations. But it seems tough when your procedure/function have EXEC and sp_executesql defined.
Any kind of help is appreciated. Thanks in advance!
The question as stated is a bit unclear so I am going to make some assumptions here. It looks like you are trying achieve the following:
First it seems you are trying to only return task in your task table where the ‘base_table’ column value references a valid SQL Server table.
Secondly if I understand the post correctly, based on the where clause condition passed to the tasks table you are trying to determine if the same columns exists in your base table.
The first part is certainly doable. However, the second part is not since it would require the query to somehow parse itself to determine what columns are being filtered on.
The following query show how you can retrieve only tasks for which there is a valid corresponding table.
SELECT *
FROM [dbo].[tasks] ts
CROSS APPLY (
SELECT [name]
FROM sys.objects WHERE object_id = OBJECT_ID('[dbo].' + QUOTENAME(ts.base_table)) AND type in (N'U')
) tb
If the field(s) you are trying to filter on is known up front (i.e. you are not trying to parse based of the tasks table) then you can modify the above query to pass the desired columns you want to check as follow:
DECLARE #columnNameToCheck NVARCHAR(50) = 'col2'
SELECT ts.*
FROM [dbo].[tasks] ts
CROSS APPLY (
SELECT [name]
FROM sys.objects WHERE object_id = OBJECT_ID('[dbo].' + QUOTENAME(ts.base_table)) AND type in (N'U')
) tb
CROSS APPLY (
SELECT [name]
FROM sys.columns WHERE object_id = OBJECT_ID('[dbo].' + QUOTENAME(ts.base_table)) AND [name] = #columnName

SQL Server doesn't use index while chekcing the value of input parameters [duplicate]

I will have a table
TABLE1
ID Name Country
1 Anna Singapore
2 Brad UK
3 Cassie US
declare #place varchar(20);
set #place='US';
select * from Table1 where country=#place;
what if the value is #place is null???
then, the select will not select anything..
what I want is to treat as there is no where statement for country. while if there's other where statements, they will work..
Any idea how to do that??
For this simple case in your question just use
IF ( #place IS NULL )
SELECT *
FROM table1
ELSE
SELECT *
FROM table1
WHERE country = #place
If your actual situation is more complex you could use
select *
from Table1
where #place is null or country=#place
option (recompile)
The reason for needing the recompile hint is to avoid having a single plan catering for both cases and doing an unnecessary scan in the event that you do supply an explicit value.
These, and other alternatives such as generating the query dynamically, are discussed in detail in the article Dynamic Search Conditions in T-SQL
like and = are equivalent in your situation because you are dealing with text (char, nchar, varchar, nvarchar) values.
Instead of
WHERE country = #place
try
WHERE country like #place
For the variable set try
Instead of
set #place='US'
Try
set #country = isnull('''US''','''%''')
To test this:
declare #country nvarchar(50)
set #country = isnull('''US''','''%''')
print #Country
set #country = isnull(NULL,'''%''')
print #Country
The extra quotes are necessary because in your example you are using explicit values - ' is an escape character and you need a single quote around each value to signify it is NOT a table column. If you were using the COLUMN_NAME you would simply use a single quote around the % character.
Depending on your SQL version you may have to use a different wildcard character.
This is fairly a fairly direct and linear solution, although there are risks associated with open variables.

Resources