Where Clause from Another table - sql

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

Related

Grabbing Null and blank Columns to a table

I'm trying to find a way to grab the count of nulls and blanks of every column in a table and put the results into a table. I found this but don't know how to push it to a table? Would also like to add in a Count(distinct fieldname) as well. Any help would be appreciated!
DECLARE #t nvarchar(max)
SET #t = N'SELECT '
SELECT #t = #t + 'sum(case when ' + c.name + ' is null or ' + c.name + ' = '''' then 1 else 0 end) "' + c.name + '",
sum(case when ' + c.name + ' is null then 0 else 1 end) "Non-Null Values for ' + c.name + '",'
FROM sys.columns c
WHERE c.object_id = object_id('TableName');
SET #t = SUBSTRING(#t, 1, LEN(#t) - 1) + ' FROM TableName;'
EXEC sp_executesql #t
**Edit
This provides the results in one row. Is there a way to do it so each field gets it's own row in a table?
FieldName CountofNullsBlanks
FieldA 0
FieldB 100
Here:
DECLARE #t nvarchar(max)
SET #t = N'SELECT '+convert(nvarchar(max),count(*))+' as [Distinct fieldnames],' -- added the distinct
SELECT #t = #t + 'sum(case when ' + c.name + ' is null or ' + c.name + ' = '''' then 1 else 0 end) "' + c.name + '",
sum(case when ' + c.name + ' is null then 0 else 1 end) "Non-Null Values for ' + c.name + '",'
FROM sys.columns c
WHERE c.object_id = object_id('YOURTABLE');
SET #t = SUBSTRING(#t, 1, LEN(#t) - 1) + 'into ##a FROM YOURTABLE;' --added the into ##a
EXEC sp_executesql #t
Notice the "into ##a" towards the end; This saves the result "table" to a global temp, which will be available until all sessions that accessed it finish. Careful with the name or it might cause conflicts in the database.

Convert static SQL to dynamic

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

Replace Quotes by ASCII chars

I have table that stores for a set of attributes from a given table the attribute name and the targetvalue to update it to.
For Example:
AttributeName | TargetValue
CustomerName | Tom
CustomerAddress | The ' road
...
As you can see it is possible that there is a single quote in a target value.
I want to have dynamic sql that creates the following output:
UPDATE T1
SET CustomerName = 'Tom',
CustomerAddress = 'The ' + CHAR(39) + ' road'
...
FROM Target T1
INNER JOIN MyList T2
ON...
So in simple words:
Create from the variable below
DECLARE #Word NVARCHAR(MAX) = 'I '' have '' quotes'
SELECT #Word
the following output:
'I ' + CHAR(39) + ' have ' + CHAR(39) + ' quotes'
Try this....
select '''' + replace(#Word, '''', ''' + CHAR(39) + ''') + ''''
That will get you the result you wanted.

Return a percentage of NULL values for a specific record

The following query returns the values of the table for each field in terms of null percentage . What I want is to get the sum of those percentages for a specific ProductID. Also, I would like to get a percentage (in an extra column) of the fields do not have value i.e. ="". Any ideas?
use AdventureWorks
DECLARE #TotalCount decimal(10,2), #SQL NVARCHAR(MAX)
SELECT #TotalCount = COUNT(*) FROM [AdventureWorks].[Production].[Product]
SELECT #SQL =
COALESCE(#SQL + ', ','SELECT ') +
'cast(sum (case when ' + QUOTENAME(column_Name) +
' IS NULL then 1 else 0 end)/#TotalCount*100.00 as decimal(10,2)) as [' +
column_Name + ' NULL %]
'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Product' and TABLE_SCHEMA = 'Production'
SET #SQL = 'set #TotalCount = NULLIF(#TotalCount,0)
' + #SQL + '
FROM [AdventureWorks].Production.Product'
print #SQL
EXECUTE SP_EXECUTESQL #SQL, N'#TotalCount decimal(10,2)', #TotalCount
You can use the following:
use AdventureWorks
DECLARE #colCount int;
DECLARE #nullCheck nvarchar(max) = N'';
DECLARE #emptyCheck nvarchar(max) = N'';
DECLARE #SQL NVARCHAR(MAX);
DECLARE #KeyToCheck int = 123; -- adapt as necessary
SELECT
#nullCheck += '
+ ' + 'count(' + QUOTENAME(column_Name) + ')'
,#emptyCheck += '
+ ' +
CASE
WHEN DATA_TYPE IN('bigint', 'int', 'smallint', 'tinyint', 'bit', 'money', 'smallmoney', 'numeric', 'decimal', 'float', 'real') THEN
-- check numeric data for zero
'sum(case when coalesce(' + QUOTENAME(column_Name) + ', 0) = 0 then 1 else 0 end)'
WHEN DATA_TYPE like '%char' or DATA_TYPE like '%text' THEN
--check character data types for empty string
'sum(case when coalesce(' + QUOTENAME(column_Name) + ', '''') = '''' then 1 else 0 end)'
ELSE -- otherwise, only check for null
'sum(case when ' + QUOTENAME(column_Name) + ' IS NULL then 1 else 0 end)'
END
,#colCount =
count(*) over()
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Product' and TABLE_SCHEMA = 'Production'
;
SET #SQL = 'SELECT case when count(*) > 0 then 100.00 - (' + #nullCheck + '
) * 100.00 / ' + cast(#colCount as nvarchar(max)) + '.00 / count(*) end as null_percent
, case when count(*) > 0 then (' + #emptyCheck + '
) * 100.00 / ' + cast(#colCount as nvarchar(max)) + '.00 / count(*) end as empty_percent
FROM Production.Product
WHERE ProductID = ' + cast(#KeyToCheck as nvarchar(max))
;
print #SQL;
EXECUTE (#SQL)
I simplified one of your expressions: Instead of sum (case when <column> IS NULL then 1 else 0 end), you can just use count(<column>). When using count with an expression instead of *, it counts the rows where this expression is non-null. As this is the opposite from what you need, I added the 100.00 - as the start of the SELECT.
For the "empty check", this would make the logic more complex to understand, hence I left the original logic there and extended it. There, I implemented an check for emptiness for numeric and character/text data types. You can easily extend that for date, binary data etc. with whichever logic you use to determine if a column is empty.
I also found it more simple to leave first + in the two variables #nullCheck and #emptyCheck, as it is valid SQL to start an expression wit this.
I also extended the statement so that if there would potentially be more than one record with ProductId = 123, it shows the average across all records, i. e. the total sum divided by the count of rows. And the outermost case expressions just avoid an division by zero error if count(*) would be zero, i. e. no record with ProductId = 123 found. In that case the return value is null.
You could use AVG function:
SELECT AVG(CASE WHEN value IS NULL THEN 100 ELSE 0 END) AS Percents
FROM Table
UPDATE:
Here is your script:
DECLARE #SQL NVARCHAR(MAX), #TABLE_NAME NVARCHAR(MAX), #TABLE_SCHEMA NVARCHAR(MAX), #PK NVARCHAR(MAX)
SET #TABLE_NAME = 'tblBigTable'
SET #TABLE_SCHEMA = 'dbo'
SET #PK = '8'
SELECT
#SQL = COALESCE(#SQL + ', ', 'SELECT ') +'AVG(CASE WHEN ' + COLUMN_NAME + ' IS NULL THEN 100 ELSE 0 END) AS [' + COLUMN_NAME +' NULL %]'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = #TABLE_SCHEMA AND
TABLE_NAME = #TABLE_NAME
SET #SQL = #SQL + ' FROM ' + #TABLE_NAME + ' WHERE pkId = ''' + #PK + ''''
print #SQL
EXECUTE SP_EXECUTESQL #SQL

T SQL Convert a row to a 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