if exists in sql within a subquery - sql

I am trying to execute the following query to check the records from the 4 tables and then calling a function,but i get an error near the brackets.It works fine if I use case,but exits when the first condition is met. I need to evaluate for all the tables in the 4 IF's:
select account_id,
(
if exists (select account_id from [dbo].[TEST_R6])
Begin
select dbo.make_indicator(cent,chem,co,dim,lg,pl,strs,vis) + space(1) + 'rr'
End
if exists (select account_id from tbl_noR6)
begin
select dbo.make_indicator(acent,chem,co,dim,lg,pl,str,vis) + space(2) + 'cc'
end
if exists (select account_id from tbl_acct)
begin
select dbo.make_indicator(cent,chem,co,dim,lg,pl,str,vis) + space(3) + 'pp'
end
if exists (select account_id from test_con)
begin
select dbo.make_indicator(cent,chem,co,dim,lg,pl,str,vis) + space(4) + 'no'
end
)as value from CRS_PRODLINE
code works partially with case statement and gives output only for the first case is met and does not check for the others:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.A_NAME)
FROM TEST_DEL c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT account_id, ' + #cols + ' from
(select account_id,
(case
when exists (select account_id from [dbo].[TEST_R6])
then
dbo.make_indicator(acent,chem,co,dim,lg,pl,str,vis) + space(1)
when exists (select account_id from tbl_noR6)
then
dbo.make_indicator(acent,chem,co,dim,lg,pl,str,vis) + space(2)
when exists (select account_id from tbl_acct)
then
dbo.make_indicator(acent,chem,co,dim,lg,pl,str,vis) + space(2)
end) as value,
assay_name
from CRS_PRODLINE
) x
pivot
(
MAX(newvalue)
for a_name in (' + #cols + ')
) p '
execute(#query)

Based on your comment that CASE only evaluates once being a problem, this should build a string of all the applicable cases.
select account_id,
(
''
+
case when exists (select account_id from dbo.[TEST_R6]) THEN select dbo.make_indicator(cent,chem,co,dim,lg,pl,strs,vis) + space(1) + 'rr' ELSE '' END
+
case when exists (select account_id from dbo.tbl_noR6) THEN select dbo.make_indicator(acent,chem,co,dim,lg,pl,str,vis) + space(2) + 'cc' ELSE '' END
+
case when exists (select account_id from dbo.tbl_acct) THEN select dbo.make_indicator(cent,chem,co,dim,lg,pl,str,vis) + space(3) + 'pp' ELSE '' END
+
case when exists (select account_id from dbo.test_con) THEN select dbo.make_indicator(cent,chem,co,dim,lg,pl,str,vis) + space(4) + 'no' ELSE '' END
) as value
from CRS_PRODLINE

First of all, if there is any record in the table your exists clause will return true. I will assume that this is intentional and you are not trying to match on a specific account_id.
If you want to use this structure you will have to use dynamic SQL. Something like this:
DECLARE #Sql VARCHAR(8000)
IF EXISTS (SELECT account_id FROM tbl_noR6)
BEGIN
SET #Sql = 'select dbo.make_indicator(acent,chem,co,dim,lg,pl,str,vis) + space(2) + ''cc'''
SET #Sql = #Sql + ')as value from CRS_PRODLINE'
EXEC #Sql
END
IF EXISTS (SELECT account_id FROM tbl_acct)
BEGIN
SET #Sql = 'select dbo.make_indicator(cent,chem,co,dim,lg,pl,str,vis) + space(3) + ''pp'''
SET #Sql = #Sql + ')as value from CRS_PRODLINE'
EXEC #Sql
END
IF EXISTS (SELECT account_id FROM test_con)
BEGIN
SET #Sql = 'select dbo.make_indicator(cent,chem,co,dim,lg,pl,str,vis) + space(4) + ''no'''
SET #Sql = #Sql + ')as value from CRS_PRODLINE'
EXEC #Sql
END

Related

Aggregate dynamic columns in SQL Server

I have a narrow table containing unique key and source data
Unique_Key
System
1
IT
1
ACCOUNTS
1
PAYROLL
2
IT
2
PAYROLL
3
IT
4
HR
5
PAYROLL
I want to be able to pick a system as a base - in this case IT - then create a dynamic SQL query where it counts:
distinct unique key in the chosen system
proportion of shared unique key with other systems. These systems could be dynamic and there are lot more than 4
I'm thinking of using dynamic SQL and PIVOT to first pick out all the system names outside of IT. Then using IT as a base, join to that table to get the information.
select distinct Unique_Key, System_Name
into #staging
from dbo.data
where System_Name <> 'IT'
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(System_Name)
FROM #staging
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Unique_Key, ' + #cols + ' into dbo.temp from
(
select Unique_Key, System_Name
from #staging
) x
pivot
(
count(System_Name)
for System_Name in (' + #cols + ')
) p '
execute(#query)
select *
from
(
select distinct Unique_Key
from dbo.data
where System_Name = 'IT'
) a
left join dbo.temp b
on a.Unique_Key = b.Unique_Key
So the resulting table is:
Unique_Key
PAYROLL
ACCOUNTS
HR
1
1
1
0
2
1
0
0
3
0
0
0
What I want is one step further:
Distinct Count IT Key
PAYROLL
ACCOUNTS
HR
3
67%
33%
0%
I can do a simple join with specific case when/sum statement but wondering if there's a way to do it dynamically so I don't need to specify every system name.
Appreciate any tips/hints.
You can try to use dynamic SQL as below, I would use condition aggregate function get pivot value then we might add OUTER JOIN or EXISTS condition in dynamic SQL.
I would use sp_executesql instead of exec to avoid sql-injection.
DECLARE #System_Name NVARCHAR(50) = 'IT'
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#parameter AS NVARCHAR(MAX);
SET #parameter = '#System_Name NVARCHAR(50)'
select DISTINCT System_Name
into #staging
from dbo.data t1
WHERE t1.System_Name <> #System_Name
SET #cols = STUFF((SELECT distinct ', SUM(IIF(System_Name = '''+ System_Name+''',1,0)) * 100.0 / SUM(IIF(System_Name = #System_Name,0,1)) ' + QUOTENAME(System_Name)
FROM #staging
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT SUM(IIF(System_Name = #System_Name,0,1)) [Distinct Count IT Key], ' + #cols + ' from dbo.data t1
WHERE EXISTS (
SELECT 1
FROM dbo.data tt
WHERE tt.Unique_Key = t1.Unique_Key
AND tt.System_Name = #System_Name
) '
EXECUTE sp_executesql #query, #parameter, #System_Name
sqlfiddle
When writing Dynamic Query, you start off with a non-dynamic query. Make sure you gets the result of the query is correct before you convert to dynamic query.
For the result that you required, the query will be
with cte as
(
select it.Unique_Key, ot.System_Name
from data it
left join data ot on it.Unique_Key = ot.Unique_Key
and ot.System_Name <> 'IT'
where it.System_Name = 'IT'
)
select [ITKey] = count(distinct Unique_Key),
[ACCOUNTS] = count(case when System_Name = 'ACCOUNTS' then Unique_Key end) * 100.0
/ count(distinct Unique_Key),
[HR] = count(case when System_Name = 'HR' then Unique_Key end) * 100.0
/ count(distinct Unique_Key),
[PAYROLL] = count(case when System_Name = 'PAYROLL' then Unique_Key end) * 100.0
/ count(distinct Unique_Key)
from cte;
Once you get the result correct, it is not that difficult to convert to dynamic query. Use string_agg() or for xml path for those repeated rows
declare #sql nvarchar(max);
; with cte as
(
select distinct System_Name
from data
where System_Name <> 'IT'
)
select #sql = string_agg(sql1 + ' / ' + sql2, ',' + char(13))
from cte
cross apply
(
select sql1 = char(9) + quotename(System_Name) + ' = '
+ 'count(case when System_Name = ''' + System_Name + ''' then Unique_Key end) * 100.0 ',
sql2 = 'count(distinct Unique_Key)'
) a
select #sql = 'with cte as' + char(13)
+ '(' + char(13)
+ ' select it.Unique_Key, ot.System_Name' + char(13)
+ ' from data it' + char(13)
+ ' left join data ot on it.Unique_Key = ot.Unique_Key' + char(13)
+ ' and ot.System_Name <> ''IT''' + char(13)
+ ' where it.System_Name = ''IT''' + char(13)
+ ')' + char(13)
+ 'select [ITKey] = count(distinct Unique_Key), ' + char(13)
+ #sql + char(13)
+ 'from cte;' + char(13)
print #sql;
exec sp_executesql #sql;
db<>fiddle demo
This solution changes the aggregation function of the PIVOT itself.
First, let's add a column [has_it] to #staging that keeps track of whether each Unique_Key has an IT row:
select Unique_Key, System_Name, case when exists(select 1 from data d2 where d2.Unique_Key=d1.Unique_Key and d2.System_Name='IT') then 1 else 0 end as has_it
into #staging
from data d1
where System_Name <> 'IT'
group by Unique_Key, System_Name
Now, the per-System aggregation (sum) of this column divided by the final total unique keys needed (example case=3) returns the requests numbers. Change the PIVOT to the following and it's ready as-is, without further queries:
set #query = ' select *
from
(
select System_Name,cnt as [Distinct Count IT Key],has_it*1.0/cnt as divcnt
from #staging
cross join
(
select count(distinct Unique_Key) as cnt
from dbo.data
where System_Name = ''IT''
)y
) x
pivot
(
sum(divcnt)
for System_Name in (' + #cols + ')
) p'

Sql join right table multiple row to column

Sql how to join right table multiple row to column
Table Application_detail
Id appl_name status
1. Abc. 1
2. DEF. 1
Table Approve_detail
applicant_id. Remark. Approveby
1. Appr by village Village
1. Appr by dist. District
I want to join like
Id. Applname. Status. Village_remark. District_remark
1. Abc. 1. Appr by village. Appr by dist
If you have hardcoded list of ApproveBys, you can do this:
SELECT ad.Id,
ad.appl_name AS Applname,
ad.status,
vil.Remark as Village_remark,
dist.Remark as Distric_remark
FROM Application_detail ad
JOIN Approve_detail vil
ON vil.applicant_id = ad.id
AND vil.Approveby = 'Village'
JOIN Approve_detail dist
ON dist.applicant_id = ad.id
AND dist.Approveby = 'District'
Otherwise you need to build a dynamic SQL. Like this:
-- Filling in initial data
SELECT *
INTO #Application_detail
FROM
(
SELECT 1 Id, 'Abc' appl_name, 1 Status
UNION ALL
SELECT 2, 'DEF', 1
) t
SELECT *
INTO #Approve_detail
FROM
(
SELECT 1 applicant_id, 'Appr by village' Remark, 'Village' Approveby
UNION ALL
SELECT 1, 'Appr by dist.', 'District'
) t
-- Start of the code
SELECT DISTINCT Approveby
INTO #Approvers
FROM #Approve_detail;
DECLARE #SQL NVARCHAR(MAX),
#SELECT NVARCHAR(MAX),
#FROM NVARCHAR(MAX),
#WHERE NVARCHAR(MAX);
SET #SELECT = 'SELECT ad.Id, ad.appl_name AS Applname, ad.status'
SET #FROM = '
FROM #Application_detail ad'
SET #WHERE = '
WHERE ad.id IN (SELECT applicant_id FROM #Approve_detail)'
SELECT #SELECT += ', t' + CAST(rn AS VARCHAR) + '.Remark AS ' + Approveby + '_remark',
#FROM += '
LEFT JOIN #Approve_detail t' + CAST(rn AS VARCHAR) + '
ON t' + CAST(rn AS VARCHAR) + '.applicant_id = ad.id
AND t' + CAST(rn AS VARCHAR) + '.Approveby = ''' + Approveby + ''''
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY Approveby) rn, Approveby FROM #Approvers
) t
SET #SQL= #SELECT + #FROM + #WHERE;
EXEC SP_EXECUTESQL #SQL

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.

Using pivot in SQL Server not returning desired output

I have two tables like this:
**tblTagDescription**
and **tblDataLog**
Now I want to show record of any specific group and in one there might be same group for multiple id in tbltagdescription. And id of tbltagdescription is foreign key for tblDataLog as TagDescID.
Here 'Group1' has 10 ID as from 1 to 10. and there might be multiple record for these ID (from 1 to 10) in tbldatalog. I want these ID from 1 to as columns. For this I used pivot:
DECLARE #COlsID NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Group NVARCHAR(50) = 'Group1'
IF OBJECT_ID('tempdb..##MYTABLE') IS NOT NULL
DROP TABLE ##MYTABLE
SELECT
#COlsID = COALESCE(#ColsID + '],[','') + CONVERT(NVARCHAR(5), z.TagDescID)
FROM
(SELECT DISTINCT TOP 50 tblDataLog.TagDescID
FROM tblDataLog
INNER JOIN tblTagDescription ON tblDataLog.TagDescID = tblTagDescription.ID
ORDER BY tblDataLog.TagDescID) z
SET #COlsID='[' + #COlsID + ']'
SET #SQL='select [DATE],SHIFT, ' + #COlsID + ' into ##MYTABLE from ( select [Date], Value,
(CASE
WHEN ((DATEPART(hour,[DATE]))>6 and (DATEPART(hour,[DATE]))<14) THEN ''A''
WHEN ((DATEPART(hour,[DATE]))>=14 and (DATEPART(hour,[DATE]))<22) THEN ''B''
WHEN ((DATEPART(hour,[DATE]))>=22 or (DATEPART(hour,[DATE]))<6) THEN ''C''
END )AS SHIFT
from tblDataLog )d pivot(max(Value) for TagDescID in (' + #COlsID + ')) piv;'
EXEC (#SQL)
Now when I execute this statement, I get an error:
Invalid column name 'TagDescID'
but there is this column in tbldatalog. How to solve this query?
You need TagDescID column in subquery.
DECLARE #COlsID NVARCHAR(MAX) = ''
DECLARE #COlsAlias NVARCHAR(MAX) = ''
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Group NVARCHAR(50) = 'Group1'
SELECT
#COlsID = #ColsID + ',' + z.TagDescID,
#COlsAlias = #COlsAlias + ',' + z.TagDescID + ' AS ' + z.ReportTag
FROM
(SELECT DISTINCT TOP 50 tblDataLog.TagDescID ID, QUOTENAME(CONVERT(NVARCHAR(5), tblDataLog.TagDescID )) TagDescID, QUOTENAME(tblTagDescription.ReportTag) ReportTag
FROM tblDataLog
INNER JOIN tblTagDescription ON tblDataLog.TagDescID = tblTagDescription.ID
ORDER BY tblDataLog.TagDescID
) z
SET #COlsID= STUFF(#COlsID,1,1,'')
SET #COlsAlias= STUFF(#COlsAlias,1,1,'')
SET #SQL='select [DATE],SHIFT, ' + #COlsAlias + ' into ##MYTABLE from ( select [Date], Value, TagDescID,
(CASE
WHEN ((DATEPART(hour,[DATE]))>6 and (DATEPART(hour,[DATE]))<14) THEN ''A''
WHEN ((DATEPART(hour,[DATE]))>=14 and (DATEPART(hour,[DATE]))<22) THEN ''B''
WHEN ((DATEPART(hour,[DATE]))>=22 or (DATEPART(hour,[DATE]))<6) THEN ''C''
END )AS SHIFT
from tblDataLog )d pivot(max(Value) for TagDescID in (' + #COlsID + ')) piv;'
EXEC (#SQL)

How to check a condition against all the columns of a table?

I have a table which has more than 30 columns(all are varchar). I need to list out all the columns which contains blank i.e.' ' values.
I tried using 'coalesce' but it is only for NULL.
The following query will give you all the columns in a table that might have null or '' values.
It is written so that you can run it for all tables in your database but you can limit it to a single table, as I have done for this specific example, checking a table called testingNulls:
--two variables needed for table name and column name, when looping through all tables
declare #table varchar(255), #col varchar(255), #sql varchar(max)
--this will be used to store the result, to have one result set instead of one row per each cursor cycle
if object_id('tempdb..#nullcolumns') is not null drop table #nullcolumns
create table #nullcolumns (tablename varchar(255), columnname varchar(255))
declare getinfo cursor for
select t.name tablename, c.name
from sys.tables t join sys.columns c on t.object_id = c.object_id
where t.name = 'testingnulls' --here the condition for the table name
open getinfo
fetch next from getinfo into #table, #col
while ##fetch_status = 0
begin
select #sql = 'if exists (select top 1 * from [' + #table + '] where [' + #col + '] is null or [' + #col + '] like '''' ) begin insert into #nullcolumns select ''' + #table + ''' as tablename, ''' + #col + ''' as all_nulls end'
print(#sql)
exec(#sql)
fetch next from getinfo into #table, #col
end
close getinfo
deallocate getinfo
--this should be the result you need:
select * from #nullcolumns
You can see a working example here. I hope this is what you need.
List all columns that contain a blank in some record? You'd use a query per column and collect the results with UNION ALL:
select 'COL1' where exists (select * from mytable where col1 like '% %')
union all
select 'COL2' where exists (select * from mytable where col2 like '% %')
union all
...
union all
select 'COL30' where exists (select * from mytable where col30 like '% %');
If you want like select * from [your_table_name] where [col1] = '' and [col2] = ''....., then use dynamic sql query like below.
Query
declare #sql as varchar(max);
select #sql = 'select * from [your_table_name] where '
+ stuff((
select ' and [' + [column_name] + '] = ' + char(39) + char(39)
from information_schema.columns
where table_name = 'your_table_name'
for xml path('')
)
, 1, 5, ''
);
exec(#sql);
Update
Or else if you want to list the column names which have a blank value, then you can use the below dynamic sql query.
Query
declare #sql as varchar(max);
select #sql = stuff((
select ' union all select ' + [column_name] + ' as [col1], '
+ char(39) + [column_name] + char(39) + ' as [col2]'
+ ' from your_table_name'
from information_schema.columns
where table_name = 'your_table_name'
for xml path('')
)
, 1, 11, ''
);
set #sql = 'select distinct t.col2 as [blank_cols] from(' + #sql
+ ')t
where coalesce(ltrim(rtrim(t.col1)), ' + char(39) + char(39) + ') = '
+ char(39) + char(39) + ';';
exec(#sql);
Find a demo here
But still I'm not sure that this is what you are looking out for.
you have not many choices but to specify all the columns in your where clause
WHERE COL1 = '' AND COL2 = '' AND COL3 = '' AND . . .
or you can use Dynamic SQL to form your query, but that is not an easy path to go
If you want to count number of columns having '' value in a table (not for each row) then use the following
SELECT max(CASE WHEN col1 = '' THEN 1 ELSE 0 END) +
max(CASE WHEN col2 = '' THEN 1 ELSE 0 END) +
max(CASE WHEN col3 = '' THEN 1 ELSE 0 END) +
...
FROM t
demo
I created a dynamic SQL script that you can use by providing the table name only
Here it is
declare #sql nvarchar(max)
declare #table sysname = 'ProductAttributes'
select #sql =
'select * from ' + #table + ' where ' +
string_agg('[' + name + '] = '' '' ', ' and ')
from sys.columns
where object_id = OBJECT_ID(#table)
select #sql
exec sp_executesql #sql
Unfortunately, for SQL string concatenation String_Agg function is new with SQL Server 2017
But it is also possible to use SQL XML Path to concatenate WHERE clause fragments
SELECT #sql = 'select * from ' + #table + ' where ' +
STUFF(
(
SELECT
' and ' + '[' + [name] + '] = '' '' '
from sys.columns
where object_id = OBJECT_ID(#table)
FOR XML PATH(''),TYPE
).value('.','VARCHAR(MAX)'
), 1, 5, ''
)
select #sql as sqlscript
exec sp_executesql #sql