Related
So the goal is to make a SQL query for table A but the where clause is stored in table B.
here is example
table B:
BatchNumber | DataItem | Operator | TValue
12345 | Branch | = | 2700
12345 | StockType| = |S
12345 | PRCode | <> |AD
from this table I need to make below where statement
select *
from testdata.TableA
where Branch = 2700 and StockType = 'S' and PRCode <> 'AD'
here are the different methods I tried using to form a sql
declare #whereClause varchar(400)
declare #schema varchar(10) = 'testdata'
declare #Batch varchar(15) = 12345
select #whereClause = N' COALESCE(#whereClause + ' ' ,'') + trim(DataItem) + ' ' + trim(Operator) + ' ' +
case
when trim(TValue) LIKE '[A-Za-z]%' then '''' + trim(TValue) + '''' + ' AND'
when trim(TValue) = ' ' then '''' + '''' + ' AND'
else trim(TValue) + ' AND'
end
from' + #schema + '.TableB where BatchNumber =' + #Batch
--remove AND from the end of statement
SET #whereClause = SUBSTRING(#whereClause,0, LEN(#whereClause) - 3)
PRINT #whereClause
However this is giving me syntax error with single codes in the case statement.
Msg 102, Level 15, State 1, Line 4
Incorrect syntax near ' ,') + trim(DataItem) + '.
I need case statement to add single quote around string operators.
I also tried different method with FOR XML PATH(''), but with this single quotes are eliminated and "<>" signs are changed to "gtlt"
really appreciate any help.
It seems like some of your single-quotations may have to be quoted or you could use variables. For example: #whereClause = N' COALESCE(#whereClause + ' ' ,'')... may require #whereClause = N' COALESCE(#whereClause + '' '' ,'''')... or something on those lines. You could store there result of COALESCE(#whereClause + ' ' ,'') in a variable and then concat it with other variables in a similar way.
Alternatively, you could write a query in SQL Server like this:
select 'select * '
union all select 'from testdata.TableA '
union all select 'where 1 = 1 '
union all
select concat(
' and ',
dataitem, ' ', operator, ' ',
case
when tvalue like '%[0-9]%' then tvalue
else concat('''', tvalue, '''')
end
)
from test /* or tableB */
where batchnumber = 12345; /*optional where clause*/
You'll get the output like this:
select *
from testdata.TableA
where 1 = 1
and Branch = 2700
and StockType = 'S'
and PRCode <> 'AD'
Example:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=12c3110a0d82e754bfc16f686779cddd
I have a static SQL query which finds out blank counts and data types (whether numeric or alphanumeric etc). Below is the query:
SELECT
case when Pattern = '' then 'BLANK' else Pattern end AS Pattern,
LEN(case when Pattern = '' then 'BLANK' else Pattern end) Length,
COUNT(*) AS Count FROM
( SELECT REPLACE(REPLACE( REPLACE( REPLACE( REPLACE(REPLACE( REPLACE( REPLACE( REPLACE(
REPLACE( REPLACE( REPLACE( REPLACE(REPLACE( REPLACE( REPLACE( REPLACE(REPLACE( REPLACE(
REPLACE( REPLACE(REPLACE( REPLACE(
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE
(REPLACE(REPLACE(REPLACE(REPLACE(UPPER(col_name),'A','A'),'B','A') ,'C','A') ,'D','A')
,'E','A'),'F','A') ,'G','A') ,'H','A') ,'I','A'),'J','A') ,'K','A') ,'L','A') ,'M','A')
,'N','A') ,'O','A') ,'P','A')
,'Q','A'),'R','A') ,'S','A') ,'T','A') ,'U','A') ,'V','A') ,'W','A') ,'X','A') ,'Y','A')
,'Z','A'),'8','N'),'7','N'),'6','N'),'5','N'),'4','N'),'3','N')
,'2','N'),'1','N'),'0','N'),'9','N')
AS Pattern FROM table_name) A GROUP BY Pattern;
How to convert this to a dynamic SQL so that I can iterate over all columns of a table?
Edit
Lets say I have a below table
CREATE TABLE Data (
Column1 varchar(50),
Column2 varchar(50),
Column3 varchar(50),
Column4 varchar(50),
)
INSERT INTO Data
(Column1, Column2, Column3, Column4)
VALUES
(NULL, NULL, 'ABC123', 'abc '),
('xyz', NULL, 'MNO300', 'XYZ123 ')
Now I want a output like below:
Count_Pat_1 --> Count is of only one data type i.e. either Alphabetic or
Numeric
Count_Part_2 --> Count is of mixed data type i.e. contains both alphabet and
number
Col_name Count_Pat_1 Count_Pat_2 Blank
Column1 1 0 1
Column2 0 0 2
Column3 0 2 0
Column4 1 1 0
How can I generate the above table? Clearly I need to resort to Dynamic SQL where the entire sql string needs to be set as variable.
DECLARE #sql nvarchar(max)
SET #sql = N''
SELECT #sql = #sql + CASE WHEN #stm = N'' THEN N'' ELSE N',' END +
'('+
'''' + c.name + ''', '+
'CASE '+
'WHEN ' + QUOTENAME(c.name) + ' IS NULL THEN 1 ' +
'ELSE 0 ' +
'END, ' +
'CASE '+
'WHEN ' + QUOTENAME(c.name) + ' IS NOT NULL THEN LEN(' +
QUOTENAME(c.name) + ') - ** the above SQL script ***
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.name = 'Data'
But inserting the above SQL script at the place as indicated by ** will not be giving the output properly.
Hence any hints would be appreciated.
SQL Server version: 2016
With dynamic SQL the trick is to get it working as static SQL first and then convert it to dynamic SQL. Some notes:
To get multiple rows, 1 for each column consider using union all.
To get a count of a case you'll need to use sum.
I've used some possible logic to get a count of one data type or mixed data type but you may find you need more precise logic.
DECLARE #Sql NVARCHAR(MAX) = '';
SELECT #Sql = #Sql
+ CASE WHEN #Sql = '' THEN 'SELECT ' ELSE ' UNION ALL SELECT ' END
+ '''' + c.[name] + ''' ColumnName'
+ ', SUM(CASE WHEN ' + QUOTENAME(c.[name]) + ' IS NOT NULL AND ' + QUOTENAME(c.[name]) + ' NOT LIKE ''%[^0-9.]%'' OR ' + QUOTENAME(c.[name]) + ' NOT LIKE ''%[0-9.]%'' THEN 1 ELSE 0 END) [Count_Pat_1]'
+ ', SUM(CASE WHEN ' + QUOTENAME(c.[name]) + ' IS NOT NULL AND ' + QUOTENAME(c.[name]) + ' LIKE ''%[^0-9.]%'' AND ' + QUOTENAME(c.[name]) + ' LIKE ''%[0-9.]%'' THEN 1 ELSE 0 END) [Count_Pat_2]'
+ ', SUM(CASE WHEN ' + QUOTENAME(c.[name]) + ' IS NULL THEN 1 ELSE 0 END) [Blank]'
+ ' FROM Data'
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.[name] = 'Data';
-- print #Sql; -- For debugging
exec sp_executesql #Sql;
Returns as requested:
ColumnName | Count_Pat_1 | Count_Pat_2 | Blank
---------------------------------------------------
Column1 | 1 | 0 | 1
Column2 | 0 | 0 | 2
Column3 | 0 | 2 | 0
Column4 | 1 | 1 | 0
So simply I'm doing something similar to:
select
[BadData], [WorseDate], [IQuitData]
into
#BadDataTempTable
from
SomeoneElsesMess
what I want to do now is something similar to:
Select
Replace(#BadDataTempTable.*, ',', ' ')
from
#BadDataTempTable -- Replace all commas in every column `with a space--`
Is this possible? If so please show me the easiest (non-function) way to do so.
Thanks. SQL Server 2012 I think. I'm using SSMS 17
No, the columns have to be specified. You could use dynamic SQL to build your update / query. Then just copy the command you want from the results.
Maybe this will help get you started:
BEGIN
-- Set the replace value
DECLARE #ls_replaceValue NVARCHAR(MAX) = ',';
-- Set the with value
DECLARE #ls_withValue NVARCHAR(MAX) = ' ';
-- Set the table name we want to query
DECLARE #ls_table NVARCHAR(MAX) = 'some_table';
-- Get all of the columns and provide the replace parameters
DECLARE #ls_columns NVARCHAR(MAX) = '';
SELECT #ls_columns = #ls_columns + ', ' + name + ' = REPLACE(' + name + ', ' + '' + '''' + REPLACE(#ls_replaceValue, '''', '''''''') + '''' + ', ' + '''' + REPLACE(#ls_withValue, '''', '''''''') + '''' + ')'
FROM sys.all_columns
WHERE object_id = OBJECT_ID(#ls_table)
AND collation_name IS NOT NULL; -- Skip columns that aren't character based
-- Remove the first ', ' from the column list
SET #ls_columns = SUBSTRING(#ls_columns, 3, LEN(#ls_columns));
IF #ls_columns = ''
BEGIN
PRINT 'Table not found'
RETURN
END
-- Build a query
DECLARE #ls_query_sql NVARCHAR(MAX) = '';
SET #ls_query_sql = 'SELECT ' + #ls_columns + ' FROM ' + #ls_table;
-- Show the results
SELECT #ls_query_sql AS querySQL;
END
Just since the OP asked about how you might do this in dynamic SQL, here's how I'd approach it. Basically get the table schema information and concatenate all the columns, plus the REPLACE logic you want using FOR XML. This basically constructs the statement Rigerta posted, but does it dynamically.
use tempdb
go
if object_id('tempdb.dbo.#SomeoneElsesBadData') is not null drop table #SomeoneElsesBadData
create table #SomeoneElsesBadData
(
BadData varchar(250),
WorseData varchar(250),
IQuitData varchar(250)
)
declare #sql nvarchar(max)
select #sql = 'select '
+ stuff((select ', '
+ name
+ ' = replace(' + name + ''','', '''')'
from tempdb.sys.columns
where object_id = object_id('tempdb.dbo.#SomeoneElsesBadData')
for xml path('')), 1, 1, '')
+ ' into #BadDataTempTable
from #SomeoneElsesBadData'
exec sp_executesql #sql
All things being equal, the data should probably be cleaned before it gets into SQL, but reality is rarely fair.
I have the following query in a stored procedure.
set #result1='EmpId,EmpName,Phone,City'
set #result2='select '+ #result1+ ' from [emptable]'+' where EmpId=1 and
EmpjoinDate= ''May-2014'''
exec(#result2)
The query returns
EmpId | EmpName | Phone | City
----------------------------------
1 | John | 832942 | NewYork
Now how to add query so that stored procedure returns single column result
| EmpInfo .........................|
------------------------------------
1,John,832942,NewYork
Please reply. Thanks.
set #result1='CONCAT(EmpId,',',EmpName,',',Phone,',',City)'
set #result2='select '+ #result1+ ' as EmpInfo from [emptable]'+' where EmpId=1 and
EmpjoinDate= ''May-2014'''
exec(#result2)
You have to build the string in the #result1 variable:
set #result1='EmpId+'',''+EmpName+'',''+Phone+'',''+City'
In Management Studio, you can also go to the Query Menu and select "Results To" -> "Results to Text" if that is enough for you.
If I get you clearly then this is who you want.
You have to concatenate the fields with the commas
set #result1='EmpId+'',''+EmpName''+'',''+''Phone''+'',''+''City'
set #result2='select '+#result1+' as EmpInfo from [emptable]'+' where EmpId=1 and
EmpjoinDate= ''May-2014'''
exec(#result2)
Using For XML and STUFF re-write the query as:
declare #result1 varchar(max),
#result2 varchar(max)
--Modify all input columns using Cast function to cast int datatype columns
--to varchar .Also this way you can add as many columns from table 'emptable'
--as you want
set #result1=''','' + cast(EmpId as varchar(20)) + '','' + EmpName + '',''
+ cast(Phone as varchar(20)) + '','' + City'
set #result2='select STUFF((SELECT '+ #result1+ ' from [emptable]
'+' where EmpId=1 FOR XML PATH('''')), 1, 1, '''') as EmpInfo'
exec(#result2)
Check Demo here
What you can do, is after receiving #result1 you replace data as you wish:
set #result1='EmpId,EmpName,Phone,City'
SET #result1 = REPLACE (#result1, ',', ' + '','' + ')
--SET #result1 = 'CONCAT ( ' + REPLACE (#result1, ',', ' , '','' , ') + ')' -- OR THIS IF you have INT inside this columns
set #result2='select '+ #result1+ ' from [emptable]'+' where EmpId=1 and
EmpjoinDate= ''May-2014'''
exec(#result2)
With CONCAT function looks better, if you have Server 2012. If not you will need CAST INT values into String
Is there a valid way in SQL Server 2005 to read a row of data into a string?
For example if my table looked like the following:
ID | First Name | Last Name | Color
-----------------------------------
1 | Timmy | Jones | pink
2 | Martha | Fisher | green
That I could get back as a result:
'1 Timmy Jones pink'
'2 Martha Fisher green'
Preferably I could get back with the column name appended such as:
'ID: 1, First Name: Timmy, Last Name: Jones, Color: pink'
'ID: 2, First Name: Martha, Last Name: Fisher, Color: green'
Update:
I'm looking for a dynamic solution so I guess querying against INFORMATION_SCHEMA would be more appropriate.
This actually helps a bit because I'm looking to set it off from an update trigger, so I'll have one row at a time and I can query against the INFORMATION_SCHEMA for the main table checking datatypes to eliminate text, ntext and image that the generated tables, 'inserted' and 'deleted' choke on.
Solution: (updated: had to add some more code to return the one value)
-- passed in table name & schema
DECLARE #table_name nvarchar(100)
DECLARE #schema_name nvarchar(100)
-- set initial value
SET #table_name = 'my_table'
SET #schema_name = 'dbo'
-- Variable to hold query
DECLARE #sql nvarchar(MAX)
SET #sql = 'SELECT #row_stringOUT = '
-- Query columns meeting criteria and build query
SELECT
-- Query as one string
#sql = (#sql + ' '' ' + COLUMN_NAME + ': '' + CASE WHEN ' + COLUMN_NAME + ' IS NULL THEN '''' WHEN ISDATE(' + COLUMN_NAME + ') = 1 THEN CONVERT(nvarchar(100), ' + COLUMN_NAME + ', 120) ELSE CAST(' + COLUMN_NAME + ' AS nvarchar(100)) END + ')
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
table_schema = #schema_name
AND
table_name = #table_name
AND
-- Filter out unuseable data types
data_type NOT IN ('text', 'ntext', 'image')
-- Trim extra character before FROM statement then add FROM statement
SELECT #sql = LEFT(#sql, LEN(#sql) - 1) + ' FROM ' + #table_name;
-- Execute Query
DECLARE #params nvarchar(MAX)
SET #params = N'#row_stringOUT ntext OUTPUT';
DECLARE #ret_value nvarchar(MAX)
EXEC sp_executesql
#sql
, #params
, #row_stringOUT = #ret_value OUTPUT
SELECT #ret_value
you can just concat the columns. if you want to get column names to you'll have to use some mix of dynamic sql, INFORMATION_SCHEMA views and querying...
SELECT cast(ID as varchar(10)) + ' ' + [First Name] + ' ' + [Last Name] + ' ' + Color
FROM MyTable
Select CAST(ID As Varchar(8)) + ' ' + [First Name] + ' ' + [Last Name] + ' ' + Color
From TheTable
To get the verbose formatting you mentioned:
Select 'ID: ' + CAST(ID As Varchar(8)) + ', First Name: ' + [First Name] + ', Last Name: ' + [Last Name] + ', Color: ' + Color
From TheTable
Edit: Now assuming ID is an integer autonumber. Most likely the case, as ajdams points out.
Actually if ID has a data type of int the solution Patrick suggested will give you a conversion error. You will have to do something like this
SELECT CAST(ID AS varchar(10)) + ' ' + [first name] + ' ' + [last name + ' ' + color
FROM MYTABLE