Multiple Dynamic Selects queries in one return - sql

I need to run multiple select count queries to count how many people are available at certain times through the day to plot into a table from ms sql server.
I have the below sql which works, but is returning each count as a new table, I would like them to all in one table on different columns.
DECLARE #Day varchar(max)
SET #Day = 'Sunday'
DECLARE #Provider varchar(max)
SET #Provider = '58611'
DECLARE #sqlText varchar(max);
SET #sqlText = N'SELECT COUNT(*) AS Available0700
FROM tblCarersRota INNER JOIN tblCarersProviders ON tblCarersProviders.CarerID = tblCarersRota.CarerID
WHERE Rotation = 2 AND tblCarersProviders.ProviderID = '''+ #Provider + ''' AND ''07:00'' between ' + #Day + 'StartTime AND ' + #Day + 'EndTime '
Exec (#sqlText)
SET #sqlText = N'SELECT COUNT(*) AS Available0800
FROM tblCarersRota INNER JOIN tblCarersProviders ON tblCarersProviders.CarerID = tblCarersRota.CarerID
WHERE Rotation = 2 AND tblCarersProviders.ProviderID = '''+ #Provider + ''' AND ''08:00'' between ' + #Day + 'StartTime AND ' + #Day + 'EndTime '
Exec (#sqlText)
Actual current result:
Available0700
21
Available0800
22
Desired result:
Available0700 || Available0800
21 || 22
I have looked at where you select (select query 1) (select query 2) but I can't get that to work with the dynamic sqltext.
How can I modify my selects to get them to all return as 1 table?
Thanks

Option 1, put it in 2 rows with UNION ALL:
SELECT COUNT(*) as Counts, 'Available0700' AvailableTime
FROM ...
UNION all
SELECT COUNT(*) as Counts, 'Available0800' AvailableTime
FROM ...
Option 2, in 2 columns with subqueries:
SELECT (SELECT COUNT(*) FROM ...) as Available0700,
(SELECT COUNT(*) FROM ...) as Available0800

I think you can store these values in variables and finally select these variables in your select query.
Below is an example code in which i have used a table variable to execute the dynamic queries and store the records and finally used a PIVOT query to fetch the require output.
DECLARE #tablevar TABLE
(
Query VARCHAR(100),
Cnt INT
)
DECLARE #sqlText varchar(max)
SET #sqlText = N'SELECT ''Available Value 1'', 100 '
INSERT INTO #tablevar
Exec (#sqlText)
SET #sqlText = N'SELECT ''Available Value 2'', 200 '
INSERT INTO #tablevar
Exec (#sqlText)
SET #sqlText = N'SELECT ''Available Value 3'', 300 '
INSERT INTO #tablevar
Exec (#sqlText)
SELECT * FROM #tablevar
PIVOT(SUM(Cnt) FOR Query IN([Available Value 1], [Available Value 2], [Available Value 3])) AS PIV
Above code is an example, please replace it with your actual code accordingly.
Thanks

Why did you use dynamic query? You can use case when inside operator COUNT in such way: COUNT(CASE WHEN <yours filter with time 7.00 > then 1 else null end) AS Available0700 , COUNT(CASE WHEN <yours filter with time 8.00 > then 1 else null end) AS Available0800.

Related

SQL - How do I get 1 empty column per row in a related table?

I am trying to write a SQL query that adds a certain amount of empty columns, based on the number of rows in a related table (t1) for a Crystal Report. These columns should have the header of the name of the dataset.
So it should look something like this:
However I would need to change the script each time a new row gets added (e.g. opening a store - not very often, but it does happen).
I thought about using the pivot function, but I believe the number of rows must be defined - plus, there is no calculation / aggregation happening.
Does anybody have an idea on how to solve this?
As Larnu already mentioned, dynamic SQL would be one way to go. I would suggest using a combination of XML PATH and dynamic SQL. Following an example:
DECLARE #colList VARCHAR(MAX) = (SELECT STUFF((SELECT ',NULL as t1_row' + cast(col1 AS varchar(3))
FROM MyTable
FOR XML PATH('')) ,1,1,'') AS Txt
)
DECLARE #stmt VARCHAR(MAX) = 'SELECT Col1, Col2, Col3, ' + #colList + ' FROM MyTable'
EXEC (#stmt)
I was able to achieve the result using dynamic SQL.
The Script looks something like this:
DECLARE #STRSQL NVARCHAR(MAX) = 'WITH a AS (SELECT ';
DECLARE #Kst nvarchar(6);
DECLARE #Markt NVARCHAR(30);
DECLARE #SCHEMA_NAME VARCHAR(50) = 'XTRADE';
DECLARE C1 CURSOR FOR
SELECT NUMMER, BEZEICHNUNG
from XTRADE.KUNDE
where NUMMER > 99 and NUMMER not in (194, 196, 198)
and (DATUM_SCHLIESSUNG > GETDATE() or DATUM_SCHLIESSUNG is null)
order by BEZEICHNUNG
OPEN C1
PRINT #Kst + ' ' + #Markt
FETCH NEXT
FROM C1 into #Kst, #Markt
while ##FETCH_STATUS = 0
BEGIN
SET #STRSQL = #STRSQL + 'null as [' + #Markt + '], '
FETCH NEXT
FROM C1 into #Kst, #Markt
END
CLOSE C1
DEALLOCATE C1;
SET #STRSQL = left(#STRSQL, len(#Strsql) - 1) + ')'
DECLARE #Statement nvarchar(max) = ', b as (select 1 as Col1, 1 as Col2, 5 as Col3 union all select 2,2,12 union all select 3, 3, 42)';
DECLARE #Exec nvarchar(max) = #STRSQL + #Statement + 'select * from b cross join a';
print #Exec;
exec sp_executesql #Exec

How to pass table name as parameter in select statement in SQL Sever

Table:
Col
------
Table1
table2
table3
Query:
select count(*)
from #tablename
I wanted to pass table1, table2, table3 as parameters for #tablename in the select query and get the count for each table
Desired output:
2 (table 1 count) 3 (table 2 count) 4 (table 3 count)
you can use dynamic sql and a cursor to run through them:
Create temp table for testing:
DECLARE #tablenametable TABLE(tablename VARCHAR(100));
INSERT INTO #tablenametable
VALUES('table1'), ('table2'), ('table3');
Use a cursor to run through all tablenames in the table
DECLARE #tablename VARCHAR(100);
DECLARE dbcursor CURSOR
FOR
SELECT tablename
FROM #tablenametable;
OPEN dbcursor;
FETCH NEXT FROM dbcursor INTO #tablename;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #sql VARCHAR(MAX);
SET #sql = 'select count(*) from '+#tablename;
PRINT(#sql);
FETCH NEXT FROM dbcursor INTO #tablename;
END;
CLOSE dbcursor;
DEALLOCATE dbcursor;
Give the following results:
select count(*) from table1
select count(*) from table2
select count(*) from table3
Just change PRINT(#SQL) to EXEC(#SQL) when your happy with it
You can use dynamic sql query.
Query
declare #sql as varchar(max);
select #sql = stuff((
select ' union all '
+ 'select cast(count(*) as varchar(100))
+ ' + char(39) + '(' + [Col] +' Count)' + char(39)
+ ' as [table_counts] '
+ ' from ' + [col]
from [your_table_name]
for xml path('')
)
, 1, 11, ''
);
exec(#sql);
Find a demo here
As mentioned here, you have to use dynamic SQL.
First approach is where you specify table name yourself:
declare #tablename varchar(30), #SQL varchar(30)
set #tablename = 'Table1' --here you specify the name
set #SQL = concat('SELECT COUNT(*) FROM ', #tablename) --here you build the query
EXEC(#SQL)
Second approach lets you use table with names of tables:
declare #SQL varchar(8000)
set #SQL = ''
declare #TableNames table(name varchar(30))
insert into #TableNames values ('Table1'), ('Table2'), ('Table3')
--here you build the query
select #SQL = #SQL + ' SELECT ''' + name + ''' AS [TableName], COUNT(*) AS [Count] FROM ' + name + ' UNION ALL' from #TableNames
-- get rid of last "UNION ALL"
set #SQL = LEFT(#SQL, LEN(#SQL) - 10)
--execute the query
EXEC(#SQL)
The result of it will be:
TableName Count
Table1 3
Table2 6
Table3 4
You can use sys.dm_db_partition_stats like this [1]:
select
t.col tableName, sum(s.row_count) tableCount
from
yourTable t
join
sys.dm_db_partition_stats s
on
(object_name(s.object_id) = t.col )
and
(s.index_id < 2)
group by
t.col;
[1]. Related answer
One line output version will be:
select
sum(s.row_count), ' ('+t.col +' count) '
from
yourTable t
join
sys.dm_db_partition_stats s
on
(object_name(s.object_id) = t.col )
and
(s.index_id < 2)
group by
t.col
for xml path('');
output:
2 (Table1 count) 3 (table2 count) 4 (table3 count)

sum columns dynamically sql

I have multiple columns with some amount in a table and I want to show the total of all those amounts in the last Total column. I have a table in sql which looks somewhat like this,
A_Amt B_Amt C_Amt D_Amt E_Amt F_Amt ...
------------------------------------------------
15 20 25 30 35 40
i have written a query as
declare #xmlResult xml=
(
select *
from Foo
for xml PATH
);
SELECT Nodes.node.value('sum(*[contains(local-name(.), "_Amt")])', 'decimal(15,2)') AS Total
FROM
#xmlResult.nodes('//row') as Nodes(node);
but the result I am getting has only one column total but i want all the columns in resultant table like A_amt etc..
This should be what you need, BUT ATTENTION! You should NOT do this. Aggregate rows should NEVER be fetched together with the "raw" data. This is - in most cases - something your UI should do (or a report...)
declare #table TABLE(ID INT IDENTITY, a INT,b INT,c INT);
insert into #table VALUES(1,1,1),(2,3,4),(5,6,7);
SELECT a,b,c
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY t.ID) AS inx
,a,b,c
FROM #table AS t
UNION SELECT 999999,SUM(a),SUM(b),SUM(c)
FROM #table
) AS tbl
ORDER BY tbl.inx
I think this is what you are looking for, try this (replace spt_values with your table) :
USE MASTER
GO
declare #lsql nvarchar(max)
declare #lsql2 nvarchar(max)
declare #yourTable nvarchar(255) = 'spt_values'
Select #lsql = isnull(#lsql+'+','') + 'Case When ISNUMERIC('+name+') = 1 Then '+name+' else 0 end' from sys.Columns where Object_id = Object_id(#yourTable)
Print #lsql
SET #lsql2 = 'Select *, '+#lsql+' as Total_allcolumns From '+#yourTable+''
Exec(#lsql2)
Using Microsoft's system table is one way to achieve dynamic SQL and thus your goal. The code below is what you want or will at least get you started.
I wasn't sure what output you expected, so I included two outputs. Just use the one you want and discard the other one. Given your question, it is probably result1. (Result1 or Result2)
!!You have to write the table name in the script at the place indicated prior to executing it!!
--DISCLAIMER
--It assume you use SQL SERVER 2012. (Probably work on 2005+ with little adjustment)
--It assume data is in a table, (Not a view for example)
--Changing SQL SERVER version may break the code as Microsoft could change "system views".
--I don't remember well, but EXEC may be limited to 4000 characters in dynamic query. (But there is a work around, just look around if you need it)
--So use at your own risk
DECLARE #objectIDTable INT,
#AllColumnAdditionStatement NVARCHAR(MAX) = '',
#TableName NVARCHAR(250) = 'WriteYourTableNameHere',--!!!OVERWRITE THE TABLE NAME HERE
#Query NVARCHAR(MAX),
#AllSumStatement NVARCHAR(MAX) = ''
SELECT TOP 1 #objectIDTable = [object_id],
#AllColumnAdditionStatement = ''
FROM sys.objects
WHERE type_desc = 'USER_TABLE'
AND name = #TableName
SELECT #AllColumnAdditionStatement = #AllColumnAdditionStatement + 'CONVERT(DECIMAL(18, 4), (CASE WHEN ISNUMERIC(' + name + ') = 1 THEN ISNULL(' + name + ', ''0'') ELSE 0 END))' + ' + ',
#AllSumStatement = #AllSumStatement + name + 'Total = SUM(CONVERT(DECIMAL(18, 4), (CASE WHEN ISNUMERIC(' + name + ') = 1 THEN ISNULL(' + name + ', ''0'') ELSE 0 END))), ' + CHAR(10)
FROM sys.columns
WHERE object_id = #objectIDTable
AND name LIKE '%_Amt' --!!!Here is a column filter/selector to sum only column ending with _Amt
SELECT #AllColumnAdditionStatement = #AllColumnAdditionStatement + '0', --just too lazy to chop off last three char
#AllSumStatement = #AllSumStatement + 'Total_ = SUM(' + #AllColumnAdditionStatement + ')' + CHAR(10),
#Query = 'SELECT *,
Total_ = ' + #AllColumnAdditionStatement +'
FROM ' + #TableName
PRINT (#Query)
/********************************************************************************************/
EXEC (#Query) --or use sp_execute if you prefer
--Result1 : addition of all selected columns into total column with all column return as well
/********************************************************************************************/
SELECT #Query = 'SELECT ' + #AllSumStatement + '
FROM ' + #TableName
EXEC (#Query) --or use sp_execute if you prefer
--Result2 : Summation of all column individualy and summation of all of them into total column
/********************************************************************************************/

SQL Server: Storing the result of a SELECT query in a float variable to be used in calculations

I want to calculate the median of a column, assign that to a dynamic variable, and use that in further calculations. This is what I've written:
declare #sqlcmd nvarchar(max)
declare #tablename nvarchar(max)
set #tablename = 'table1'
-- calculate median
declare #median float
set #sqlcmd =
'SELECT
((SELECT MAX(field1) FROM
(SELECT TOP 50 PERCENT field1 FROM ' + #tablename + ' Where field1 is not null
ORDER BY field1) AS BottomHalf)
+
(SELECT MIN(field1) FROM
(SELECT TOP 50 PERCENT field1FROM ' + #tablename + ' Where field1 is not null
ORDER BY field1 DESC) AS TopHalf)
) / 2'
execute sp_executesql #sqlcmd
set #median = ???
-- calculate value2 as value 1 minus median
'update ' + #tablename +
' set value2 = value 1 - ' + #median
execute sp_executesql #sqlcmd
The result of my experimentation is either:
"Error converting data type nvarchar to float."
Or if I remove the second calculation, it simply pops up the result to the result window.
Using SQL Server 2012. Thank you in advance!
Just convert your #median to VARCHAR when your are updating
SET #sqlcmd = 'update ' + #tablename +
' set value2 = value 1 - ' + CONVERT(VARCHAR(100), #median)
EXEC(#sqlcmd )
Try to avoid dynamic SQL statements (concatenating a string of SQL and then executing it). That may be causing the "nvarchar to float" error. Declare your variables, then set them in select statements:
DECLARE #max float
DECLARE #min float
SELECT #max = MAX(field1) FROM table1
SELECT #min = Min(field1) FROM table1
—- use #max and #min in other statements
Take a look at this question for ideas on how to get median in SQL Server.
You also may want to look at this question on how to get a result of dynamic SQL into a variable.
In the end I went with the strategy of passing the output of a select query into an update statement using a temporary table with the guidance of this article: https://smehrozalam.wordpress.com/2009/10/14/t-sql-using-result-of-a-dynamic-sql-query-in-a-variable-or-table/.
The result looks something like this:
declare #sqlcmd nvarchar(max)
declare #tablename nvarchar(max)
--insert tablename here
set #tablename = 'table1'
-- create temporary table to store median results to pass to equation
create table temptable (calc_Result(12,10))
-- calculate medians
insert into temptable exec
(N'SELECT
((SELECT MAX(field1) FROM
(SELECT TOP 50 PERCENT field1 FROM results_' + #tablename + ' Where field1 is not null ORDER BY field1) AS BottomHalf)
+
(SELECT MIN(field1) FROM
(SELECT TOP 50 PERCENT field1 FROM results_' + #tablename + ' Where field1 is not null ORDER BY field1 DESC) AS TopHalf)
) / 2 AS Field1_Median)')
-- calculate Field1 - Median
set #sqlcmd =
'update results_' + #tablename +
' set Field1_Minus_Median = Field1 - (select Feild1_Median from temptable )'
execute sp_executesql #sqlcmd
Thank you for all the help!

Can I write a query has a conditional table selection

We have 2 tables with identical structure and based on a variable I want to choose which table to select on with out having to write 2 queries in my procedure.
Is this possible?
I tried
declare #table int
set #table = 1
Select orderID, Quantity
from case when #table = 1 then tblOrders else tblSubscriptionOrders end
where filled = 0
But that did not work
You would need to use dynamic SQL for this (assuming you want to scale it to more than just 2 tables), which would work but is suboptimal as SQL will not generate statistics for it and have a harder time optimizing the query.
declare #table sysname
declare #SQL varchar(1000)
set #table = 'MyTable'
SET #SQL='SELECT orderID, Quantity FROM ' + QUOTENAME(#table) + ' WHERE filled=0'
exec sp_executesql #SQL
or, in a stored procedure:
CREATE PROCEDURE p_ConditionalSelect #table sysname
as
declare #SQL varchar(1000)
set #table = 'MyTable'
SET #SQL='SELECT orderID, Quantity FROM ' + QUOTENAME(#table) + ' WHERE filled=0'
exec sp_executesql #SQL
If it's just two tables you could do:
Declare #table = 1
SELECT *
FROM Table1
WHERE <stuff>
AND #Table = 1
UNION ALL
SELECT *
FROM Table2
WHERE <stuff>
AND #Table = 2
The filter on #table will result in only one of the two halves showing data.
One option is to use Dynamic SQL but if performance isn't an immediate issue, much simpler is to just UNION the tables and add a dummy [table] column to select from.
SELECT orderID, Quantity
FROM (
SELECT [table] = 1, orderID, Quantity
FROM tblOrders
UNION ALL
SELECT [table] = 2, orderID, Quantity
FROM tblSubscriptionOrders
) t
WHERE t.Table = #table