Using a string of quoted values in a variable for a SQL WHERE CLAUSE - sql

The answer escapes me...maybe because it is not possible...
Example that works...
SELECT * FROM TABLEA WHERE FIELD1 IN ('aaa','bbb','ccc')
Example that does not work...
Attempt to leverage variable so that I can define the values once in a string of statements
DECLARE #ListValues VARCHAR(50)
SET #ListValues = '''aaa'',''bbb'',''ccc'''
SELECT * FROM TABLEA WHERE FIELD1 IN (#ListValues)
This is is obviously only a small part of the equation and for other reasons...
I cannot leverage a table for the values and change this to a true sub-query
The closest question I could find was this one... but does not cover my requirements obviously...
Storing single quotes in varchar variable SQL Server 2008
Thanks in advance.

You can do this using dynamic SQL:
DECLARE #ListValues VARCHAR(MAX)
,#SQL VARCHAR(MAX)
SELECT #ListValues = '''aaa'',''bbb'',''ccc'''
,#SQL = 'SELECT * FROM TABLEA WHERE FIELD1 IN ('+#ListValues+')'
EXEC (#SQL)

It doesn't work because the IN operator expects a list of items - here strings.
What you're supplying with your #ListValues variable however is a single string - not a list of strings.
What you could do is use a table variable and store your values in it:
DECLARE #ListOfValues TABLE (ItemName VARCHAR(50))
INSERT INTO #ListOfValues(ItemName)
VALUES('aaa'), ('bbb'), ('ccc')
SELECT *
FROM TABLEA
WHERE FIELD1 IN (SELECT ItemName FROM #ListOfValues)

Build your whole SQL query dynamically (say it's stored in a string variable #sql),
and then execute it with EXEC (#sql). Better yet, use the sp_executesql SP
because this approach is more secure (less prone to SQL injection) than EXEC.
See: sp_executesql

The IN operator in SQLServer expect a list of values, your variable is a single string, the query parsed will be different
SELECT * FROM TABLEA WHERE FIELD1 IN ('aaa','bbb','ccc')
SELECT * FROM TABLEA WHERE FIELD1 IN ("'aaa','bbb','ccc'")
Attention: the double quotes are there only for readability, to get the string with single quote in it.
if you know a programming language the first one is like searching in an array, the second is a string.
To store a list in your variable it need to a table
DECLARE #varTable TABLE (field1 varchar())
So that you can use it in your IN
SELECT * FROM TABLEA WHERE FIELD1 IN (SELECT field1 FROM #varTable)
To add values to the table variable use an INSERT statament like usual for tables.

Related

How can I replace multiple words of a string in SQL

Is it possible to replace multiple words in a string in sql without using multiple replace functions?
For example I have a string where I need to replace word 'POLYESTER' with 'POLY' , 'COTTON' with 'CTN', 'GRAPHIC' with 'GRPHC' etc in order to keep the string length at a max of say 30 without much loosing the readability of contents in it(can't use substring to limit chars since it can trim the end meaningful parts of string completely). So we decided to short some keywords like above.
Current query I have used :
SELECT
REPLACE(REPLACE('**Some string value **COTTON **Some string value ** POLYESTER', 'POLYESTER', 'POLY'), 'COTTON', 'CTN')
If I have 10 keywords like this, what will be the best way to achieve the result other than using multiple replace function. I am using SQL Server 2012.
considering sql server is your only instrument (not a c# or another application), as a workaroud; use a temp or persistent table to store replacement options.
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
DROP TABLE #tmp
CREATE TABLE #tmp (
fromText VARCHAR(16),
toText VARCHAR(16)
);
INSERT INTO #tmp (fromText, toText)
VALUES
('POLYESTER', 'POLY'),
('COTTON', 'CTN'),
('GRAPHIC', 'GRPHC')
DECLARE #someValue AS NVARCHAR(MAX) =
'**Some string value **COTTON **Some string value ** POLYESTER';
SELECT #someValue = REPLACE(#someValue, fromText, toText) FROM #tmp;
PRINT #someValue
and the result is:
**Some string value **CTN **Some string value ** POLY.
The answer of mehmetx is actually very nice.
If you need your replacement functionality on a regular basis, you could think about using a normal table instead of a temporary table.
But if you need this logic only once in a while, and performance is not much of an issue, you could avoid the additional replacements table altogether and use a table expression in the FROM clause instead. Something like this:
DECLARE #someValue AS NVARCHAR(MAX) = '**Some string value **COTTON **Some string value ** POLYESTER';
SELECT #someValue = REPLACE(#someValue, fromText, toText)
FROM
(VALUES
('POLYESTER', 'POLY'),
('COTTON', 'CTN'),
('GRAPHIC', 'GRPHC')
) AS S (fromText, toText);
EDIT:
I noticed, that this logic regrettably does not work as expected when used in an UPDATE statement to update existing data in a table.
For that purpose (if needed), I created a user-defined function that performs the replacement logic. I called it MultiReplace. And it does not use the replacement data from a temporary table, but from a "normal" table, which I called Replacements.
The following code demonstrates it. It uses a data table called MyData, which gets updated with all replacements in the Replacements table using the MultiReplace function:
IF OBJECT_ID('MultiReplace') IS NOT NULL
DROP FUNCTION MultiReplace;
IF OBJECT_ID('Replacements') IS NOT NULL
DROP TABLE Replacements;
IF OBJECT_ID('MyData') IS NOT NULL
DROP TABLE MyData;
GO
CREATE TABLE Replacements (
fromText VARCHAR(100),
toText VARCHAR(100)
);
CREATE TABLE MyData (
SomeValue VARCHAR(MAX)
)
GO
CREATE FUNCTION MultiReplace(#someValue AS VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
SELECT #someValue = REPLACE(#someValue, fromText, toText) FROM Replacements;
RETURN #someValue;
END;
GO
INSERT INTO MyData (SomeValue)
VALUES
('**Some string value **COTTON **Some string value ** POLYESTER');
INSERT INTO Replacements (fromText, toText)
VALUES
('POLYESTER', 'POLY'),
('COTTON', 'CTN'),
('GRAPHIC', 'GRPHC');
SELECT * FROM MyData;
UPDATE MyData SET SomeValue = dbo.MultiReplace(SomeValue)
SELECT * FROM MyData;
But perhaps using multiple REPLACE statements might be more straightforward after all?...
EDIT 2:
Based on the short conversation in the comments, I could propose a simpler solution that uses multiple REPLACE statements in a clearer way. I have only tested it on SQL Server 2019; I am not sure if it will work correctly on SQL Server 2012.
Again, I use a table called MyData for testing here. But there are no additional database objects anymore.
Regrettably, I did not get it to work with a temporary table containing the replacement values.
-- Preparations:
IF OBJECT_ID('MyData') IS NOT NULL
DROP TABLE MyData;
CREATE TABLE MyData (
SomeValue VARCHAR(MAX)
);
INSERT INTO MyData
VALUES
('**Some string value **COTTON **Some string value ** POLYESTER'),
('**Another string value **GRAPHIC **Another string value ** POLYESTER');
-- Actual work:
SELECT * FROM MyData; -- Show the state before updating
DECLARE #someValue VARCHAR(MAX);
UPDATE MyData
SET
#someValue = SomeValue,
#someValue = REPLACE(#someValue, 'POLYESTER', 'POLY'),
#someValue = REPLACE(#someValue, 'COTTON', 'CTN'),
#someValue = REPLACE(#someValue, 'GRAPHIC', 'GRPHC'),
SomeValue = #someValue;
SELECT * FROM MyData; -- Show the state after updating

SQL - convert string to maths expression

I have a column that contains arithmetic expressions, say '1+2+3'.
How (if?) can I make SQL interpret the string as a statement?
"SELECT 1+2+3" will result in 6.
Any possible way to SELECT CAST(CONVERT(WHATEVERVOODOOMAGIC('1+2+3'...
to get 6 without creating stored functions?
Played with
EXEC('SELECT'...)
but still no success.
try this,
SELECT [1+2+3]
it works
create table #tmp (col1 varchar(10))
insert into #tmp values ('1+2+3')
declare #dynamic varchar(1000);
select #dynamic = 'select ' + col1 from #tmp
execute (#dynamic)
drop table #tmp

SELECT #local_variable=values from table where values can have multiple values

I am using below query
SELECT #local_variable=Adtid from table where Adtid can have multiple values stored into it. As I don't know what to use instead of '=' in #local_variable=Adtid. Can anyone suggest please what I should use instead of '=' so that my local varaible can have all values of Adtid
The variable can't actually hold multiple values. You can declare a table variable instead, where you can then do something like
declare #tableVariable table
(
Adtid int
);
insert into #tableVariable
select Adtid from table where Adtid ...
This puts the relevant rows inside your table variable. Now you can use the table variable to eg. create a cursor (to go row by row in the data - you can also do that on the original select if you only need to go through once) or use it in a join clause.
You can use below syntax for get the Adtid as comma separator in result
DECLARE #local_variable VARCHAR(100) =''
SELECT #local_variable += CAST(Adtid AS VARCHAR) + ',' FROM TABLE
PRINT #local_variable
and another way as per below answer.

SQL Server - Replacing Single Quotes and Using IN

I am passing a comma-delimited list of values into a stored procedure. I need to execute a query to see if the ID of an entity is in the comma-delimited list. Unfortunately, I think I do not understand something.
When I execute the following stored procedure:
exec dbo.myStoredProcedure #myFilter=N'1, 2, 3, 4'
I receive the following error:
"Conversion failed when converting the varchar value '1, 2, 3, 4' to data type int."
My stored procedure is fairly basic. It looks like this:
CREATE PROCEDURE [dbo].[myStoredProcedure]
#myFilter nvarchar(512) = NULL
AS
SET NOCOUNT ON
BEGIN
-- Remove the quote marks so the filter will work with the "IN" statement
SELECT #myFilter = REPLACE(#myFilter, '''', '')
-- Execute the query
SELECT
t.ID,
t.Name
FROM
MyTable t
WHERE
t.ID IN (#myFilter)
ORDER BY
t.Name
END
How do I use a parameter in a SQL statement as described above? Thank you!
You could make function that takes your parameter, slipts it and returns table with all the numbers in it.
If your are working with lists or arrays in SQL Server, I recommend that you read Erland Sommarskogs wonderful stuff:
Arrays and Lists in SQL Server 2005
You need to split the string and dump it into a temp table. Then you join against the temp table.
There are many examples of this, here is one at random.
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/01/t-sql-split-function.aspx
Absent a split function, something like this:
CREATE PROCEDURE [dbo].[myStoredProcedure]
#myFilter varchar(512) = NULL -- don't use NVARCHAR for a list of INTs
AS
SET NOCOUNT ON
BEGIN
SELECT
t.ID,
t.Name
FROM
MyTable t
WHERE
CHARINDEX(','+CONVERT(VARCHAR,t.ID)+',',#myFilter) > 0
ORDER BY
t.Name
END
Performance will be poor. A table scan every time. Better to use a split function. See: http://www.sommarskog.se/arrays-in-sql.html
I would create a function that takes your comma delimited string and splits it and returns a single column table variable with each value in its own row. Select that column from the returned table in your IN statement.
I found a cute way of doing this - but it smells a bit.
declare #delimitedlist varchar(8000)
set #delimitedlist = '|1|2|33|11|3134|'
select * from mytable where #delimitedlist like '%|' + cast(id as varchar) + '|%'
So... this will return all records with an id equal to 1, 2, 33, 11, or 3134.
EDIT:
I would also add that this is not vulnerable to SQL injection (whereas dynamic SQL relies on your whitelisting/blacklisting techniques to ensure it isn't vulnerable). It might have a performance hit on large sets of data, but it works and it's secure.
I have a couple of blog posts on this as well, with a lot of interesting followup comments and dialog:
More on splitting lists
Processing list of integers

SQL Server 2008 query to find rows containing non-alphanumeric characters in a column

I was actually asked this myself a few weeks ago, whereas I know exactly how to do this with a SP or UDF but I was wondering if there was a quick and easy way of doing this without these methods. I'm assuming that there is and I just can't find it.
A point I need to make is that although we know what characters are allowed (a-z, A-Z, 0-9) we don't want to specify what is not allowed (##!$ etc...). Also, we want to pull the rows which have the illegal characters so that it can be listed to the user to fix (as we have no control over the input process we can't do anything at that point).
I have looked through SO and Google previously, but was unable to find anything that did what I wanted. I have seen many examples which can tell you if it contains alphanumeric characters, or doesn't, but something that is able to pull out an apostrophe in a sentence I have not found in query form.
Please note also that values can be null or '' (empty) in this varchar column.
Won't this do it?
SELECT * FROM TABLE
WHERE COLUMN_NAME LIKE '%[^a-zA-Z0-9]%'
Setup
use tempdb
create table mytable ( mycol varchar(40) NULL)
insert into mytable VALUES ('abcd')
insert into mytable VALUES ('ABCD')
insert into mytable VALUES ('1234')
insert into mytable VALUES ('efg%^&hji')
insert into mytable VALUES (NULL)
insert into mytable VALUES ('')
insert into mytable VALUES ('apostrophe '' in a sentence')
SELECT * FROM mytable
WHERE mycol LIKE '%[^a-zA-Z0-9]%'
drop table mytable
Results
mycol
----------------------------------------
efg%^&hji
apostrophe ' in a sentence
Sql server has very limited Regex support. You can use PATINDEX with something like this
PATINDEX('%[a-zA-Z0-9]%',Col)
Have a look at PATINDEX (Transact-SQL)
and Pattern Matching in Search Conditions
I found this page with quite a neat solution. What makes it great is that you get an indication of what the character is and where it is. Then it gives a super simple way to fix it (which can be combined and built into a piece of driver code to scale up it's application).
DECLARE #tablename VARCHAR(1000) ='Schema.Table'
DECLARE #columnname VARCHAR(100)='ColumnName'
DECLARE #counter INT = 0
DECLARE #sql VARCHAR(MAX)
WHILE #counter <=255
BEGIN
SET #sql=
'SELECT TOP 10 '+#columnname+','+CAST(#counter AS VARCHAR(3))+' as CharacterSet, CHARINDEX(CHAR('+CAST(#counter AS VARCHAR(3))+'),'+#columnname+') as LocationOfChar
FROM '+#tablename+'
WHERE CHARINDEX(CHAR('+CAST(#counter AS VARCHAR(3))+'),'+#columnname+') <> 0'
PRINT (#sql)
EXEC (#sql)
SET #counter = #counter + 1
END
and then...
UPDATE Schema.Table
SET ColumnName= REPLACE(Columnname,CHAR(13),'')
Credit to Ayman El-Ghazali.
SELECT * FROM TABLE_NAME WHERE COL_NAME LIKE '%[^0-9a-zA-Z $#$.$-$''''$,]%'
This works best for me when I'm trying to find any special characters in a string