isnull for dynamically Generated column - sql

I am getting temp table with dynamically generated columns let say it is columns A,B,C,D etc from other source.
Now in my hand I have temp table with column generated. I had to write stored procedure with the use of temp table.
So my stored procedure is like
create proc someproc()
as
begin
Insert into #searchtable
select isnull(#temp.*,0.00)
End
Now #searchresult is table created by me to store temp table columns. The problem arises when I want to check isnull for #tempdb columns. Because from source it comes it may be 3 columns, again next time it may be 4 columns. It changes.
Since it is dynamically generated I cannot use each column name and use like below:
isnull(column1,0.00)
isnull(column2,0.00)
I had to use all column generated and check if value is empty use 0.00
I tried this below but not working:
isnull(##temp.*,0.00),

Try with Dynamic code by fetching the column name for your dynamic table from [database].NFORMATION_SCHEMA.COLUMNS
--Get the Column Names for the your dynamic table and add the ISNULL Check:
DECLARE #COLS VARCHAR(MAX) = ''
SELECT #COLS = #COLS + ', ISNULL(' + COLUMN_NAME + ', 0.00) AS ' + COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE '#temp[_]%' -- Dynamic Table (here, Temporary table)
DECLARE #COLNAMES VARCHAR(MAX) = STUFF(#COLS, 1, 1, '')
--Build your Insert Command:
DECLARE #cmd VARCHAR(MAX) = '
INSERT INTO #temp1
SELECT ' + #COLNAMES + ' FROM #temp'
--Execute:
EXEC (#cmd)

Hope, I understood your comment right:
CREATE PROCEDURE someproc
AS
IF OBJECT_ID(N'#searchtable') IS NOT NULL DROP TABLE #searchtable
IF OBJECT_ID(N'#temp') IS NOT NULL
BEGIN
DECLARE #sql nvarchar(max),
#cols nvarchar(max)
SELECT #cols = (
SELECT ',COALESCE('+QUOTENAME([name])+',0.00) as '+QUOTENAME([name])
FROM sys.columns
WHERE [object_id] = OBJECT_ID(N'#temp')
FOR XML PATH('')
)
SELECT #sql = N'SELECT '+STUFF(#cols,1,1,'')+' INTO #searchtable FROM #temp'
EXEC sp_executesql #sql
END
This SP checks if #temp table exists. If exists then it takes all column names from sys.columns table and we make a string like ,COALESCE([Column1],0.00) as [Column1], etc. Then we make a dynamic SQL query like:
SELECT COALESCE([Column1],0.00) as [Column1] INTO #searchtable FROM #temp
And execute it. This query result will be stored in #searchtable.
Notes: Use COALESCE instead of ISNULL, and sp_executesql instead of direct exec. It is a good practice.

Related

How can i extend the code to be able to show drop list of values from selected column in SSRS report

I'm new to sql and i'm trying to create SSRS.
I found this code in internet to create SSRS report and it works good to me. However i need to adjust this code to get the value as well from selected column
USE [project]
GO
/****** Object: StoredProcedure [dbo].[Report] Script Date: 26-1-2020 01:19:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[Report]
#SchemaName VARCHAR(128)='sys',
#TableName VARCHAR(128)='columns',
#ColumnList VARCHAR(MAX)='object_id,column_id,name,max_length,system_type_id'
AS
BEGIN
DECLARE #ColumnNames VARCHAR(MAX)
DECLARE #ColumnNamesVAR VARCHAR(MAX)
--drop ##Temp_Data Table
IF OBJECT_ID('tempdb..##Temp_Data') IS NOT NULL
DROP TABLE ##Temp_Data
--drop ##Temp_Data_Final Table
IF OBJECT_ID('tempdb..##Temp_Data_Final') IS NOT NULL
DROP TABLE ##Temp_Data_Final
--drop #Temp_Columns Table
IF OBJECT_ID('tempdb..#Temp_Columns') IS NOT NULL
DROP TABLE #Temp_Columns
Create table #ColumnList (Data NVARCHAR(MAX))
insert into #ColumnList values (#ColumnList)
--convert all column list to VARCHAR(1000) for unpivot
;with Cte_ColumnList as (
SELECT
'['+LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)')))+']' AS ColumnList
FROM
(
SELECT CAST('<XMLRoot><RowData>' + REPLACE(Data,',','</RowData><RowData>')
+ '</RowData></XMLRoot>' AS XML) AS x
FROM #ColumnList
)t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n))
,CTE_ColumnListVarchar as
(Select 'CAST('+ColumnList+' as VARCHAR(1000)) AS '+ColumnList AS ColumnListVAR,ColumnList from Cte_ColumnList)
SELECT #ColumnNamesVAR = COALESCE(#ColumnNamesVAR + ', ', '') + ColumnListVAR,
#ColumnNames = COALESCE(#ColumnNames + ', ', '') + ColumnList
FROM CTE_ColumnListVarchar
--Insert data into ##Temp_Data Table
DECLARE #SQL NVARCHAR(MAX)
DECLARE #TempTbleSQL NVARCHAR(MAX)
SET #TempTbleSQL='Select ROW_NUMBER()
OVER (order by (Select 1)) AS R,'+#ColumnNames +' into ##Temp_Data from ['+#SchemaName+'].['+#TableName+']'
--Print #TempTbleSQL
EXEC(#TempTbleSQL)
SET #SQL='
select
R,columnname,value into ##Temp_Data_Final from
(select R,'+#ColumnNamesVAR+' from ##Temp_Data )u
unpivot
(value for columnname in ('+#ColumnNames+'))v'
--Print #SQL
EXEC(#SQL)
Select * From ##Temp_Data_Final
END
SO, Now i can select Schema, Table & column. but i couldn't know how i get drop list for values in selected column.
And one more thing. how i can deploy this report to web form.Or if there any way to create dynamic sql with cascading parameters where i can select schema, table, column and values
PLEASE SOMEBODY HELP ME WITH THIS IT REALLY IMPORTANT
Here i can choose Schema, then table and the column. So i want to extend the code to be able to get another drop list with value of selected column
I used also the following datasets for each parameter
--ds_schema
SELECT NAME AS schemaname FROM sys.schemas
WHERE NAME not in (
'guest',
'information_schema',
'sys',
'db_owner',
'db_accessadmin',
'db_securityadmin',
'db_ddladmin',
'db_backupoperator',
'db_datareader',
'db_datawriter',
'db_denydatareader',
'db_denydatawriter')
----DSTables
Select Distinct Table_Name as TableName from INFORMATION_SCHEMA.TABLES
where TABLE_SCHEMA=#SchemaName
order by Table_Name
----DS_Columns
Select COLUMN_NAME as ColumnName from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA=#SchemaName
and TABLE_NAME=#TableName
To get a list of values in a column you need to build a SQL statement then execute it.
As you have your parameters you can do something like this...
SET #SQL = 'SELECT DISTINCT ' + QUOTENAME(#ColumnName) + ' FROM ' + QUOTENAME(#SchemaName) + '.' + QUOTENAME(#TableName) + ' ORDER BY ' + QUOTENAME(#ColumnName)
EXEC (#SQL)
notes
This gives a DISTINCT list of values and also sorts them using the ORDER BY clause, just edit the SET #SQL = line to adjust the query that is executed.
I've used QUOTENAME() to put square brackets around the schema, table and column names e.g. SELECT DISTINCT [myColumnName] FROM .....
You can add PRINT #SQL at the end to see the generated SQL if you like.

i want to fetch data according to tables which are dynamic means their name might change in databse from time to time

I am working on a query, where I need to fetch data from tables which are dynamic in nature, meaning their name might change in the database from time to time.
So, first step is that from the schema, I will get to know what the recent table names are under that schema from which I have to fetch the data, but the second part is the difficult one for me: Once I've got the table name, how could I use those tables to fetch data inside them?
You can use dynamic SQL for that
use tempdb
GO
CREATE TABLE dynamic_table
(
a INT,
b INT);
GO
INSERT INTO dynamic_table VALUES (1,1);
GO
DECLARE #table_name VARCHAR(200) = 'dynamic_table';
DECLARE #sql VARCHAR(MAX),
#columns VARCHAR(MAX);
SELECT #columns =
d FROM (
SELECT DISTINCT SUBSTRING(
(
SELECT ',' + ds1.name AS [text()]
FROM sys.columns ds1
WHERE ds1.object_id = ds2.object_id
ORDER BY ds1.name FOR XML PATH('')
), 2, 1000) AS d
FROM sys.columns ds2 WHERE ds2.object_id = OBJECT_ID(#table_name)
) a;
SET #sql = 'SELECT ' + #columns + ' FROM ' + #table_name;
EXEC (#sql)
DROP TABLE dynamic_table
GO
Use the below one and change as per your requirement, Hope it's helpful.
Declare #Table varchar(100)
Declare #Sql nvarchar(max)
Set #Table = 'etl_run'
Set #Sql = 'Select * from ' + #Table
exec (#Sql)

Dynamic SQL Result INTO Temporary Table

I need to store dynamic sql result into a temporary table #Temp.
Dynamic SQL Query result is from a pivot result, so number of columns varies(Not fixed).
SET #Sql = N'SELECT ' + #Cols + ' FROM
(
SELECT ResourceKey, ResourceValue
FROM LocaleStringResources where StateId ='
+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =''' + LTRIM(RTRIM(#CultureCode)) + '''
) x
pivot
(
max(ResourceValue)
for ResourceKey IN (' + #Cols + ')
) p ;'
--#Cols => Column Names which varies in number
Now I have to insert dynamic sql result to #Temp Table and use this #Temp Table with another existing table to perform joins or something else.
(#Temp table should exist there to perform operations with other existing tables)
How can I Insert dynamic SQL query result To a Temporary table?
Thanks
Can you please try the below query.
SET #Sql = N'SELECT ' + #Cols + '
into ##TempTable
FROM
(
SELECT ResourceKey, ResourceValue
FROM LocaleStringResources where StateId ='
+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =''' + LTRIM(RTRIM(#CultureCode)) + '''
) x
pivot
(
max(ResourceValue)
for ResourceKey IN (' + #Cols + ')
) p ;'
You can then use the ##TempTable for further operations.
However, do not forget to drop the ##TempTable at the end of your query as it will give you error if you run the query again as it is a Global Temporary Table
As was answered in (https://social.msdn.microsoft.com/Forums/sqlserver/en-US/144f0812-b3a2-4197-91bc-f1515e7de4b9/not-able-to-create-hash-table-inside-stored-proc-through-execute-spexecutesql-strquery?forum=sqldatabaseengine),
you need to create a #Temp table in advance:
CREATE TABLE #Temp(columns definition);
It seems that the task is impossible, if you know nothing about the dynamic list of columns in advance. But, most likely you do know something.
You do know the types of dynamic columns, because they come from PIVOT. Most likely, you know the maximum possible number of dynamic columns. Even if you don't, SQL Server has a limit of 1024 columns per (nonwide) table and there is a limit of 8060 bytes per row (http://msdn.microsoft.com/en-us/library/ms143432.aspx). So, you can create a #Temp table in advance with maximum possible number of columns and use only some of them (make all your columns NULLable).
So, CREATE TABLE will look like this (instead of int use your type):
CREATE TABLE #Temp(c1 int NULL, c2 int NULL, c3 int NULL, ..., c1024 int NULL);
Yes, column names in #Temp will not be the same as in #Cols. It should be OK for your processing.
You have a list of columns in your #Cols variable. You somehow make this list of columns in some external code, so when #Cols is generated you know how many columns there are. At this moment you should be able to generate a second list of columns that matches the definition of #Temp. Something like:
#TempCols = N'c1, c2, c3, c4, c5';
The number of columns in #TempCols should be the same as the number of columns in #Cols. Then your dynamic SQL would look like this (I have added INSERT INTO #Temp (#TempCols) in front of your code):
SET #Sql = N'INSERT INTO #Temp (' + #TempCols + N') SELECT ' + #Cols + N' FROM
(
SELECT ResourceKey, ResourceValue
FROM LocaleStringResources where StateId ='
+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =''' + LTRIM(RTRIM(#CultureCode)) + '''
) x
pivot
(
max(ResourceValue)
for ResourceKey IN (' + #Cols + ')
) p ;'
Then you execute your dynamic SQL:
EXEC (#Sql) OR sp_executesql #Sql
And then do other processing using the #Temp table and temp column names c1, c2, c3, ...
MSDN says:
A local temporary table created in a stored procedure is dropped
automatically when the stored procedure is finished.
You can also DROP the #Temp table explicitly, like this:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp'
All this T-SQL code (CREATE TABLE, EXEC, ...your custom processing..., DROP TABLE) would naturally be inside the stored procedure.
Alternative to create a temporary table is to use the subquery
select t1.name,t1.lastname from(select * from table)t1.
where "select * from table" is your dyanmic query. which will return result which you can use as temp table t1 as given in example .
IF OBJECT_ID('tempdb..##TmepTable') IS NOT NULL DROP TABLE ##TmepTable
CREATE TABLE ##TmepTable (TmpCol CHAR(1))
DECLARE #SQL NVARCHAR(max) =' IF OBJECT_ID(''tempdb..##TmepTable'') IS NOT
NULL DROP TABLE ##TmepTable
SELECT * INTO ##TmepTable from [MyTableName]'
EXEC sp_executesql #SQL
SELECT Alias.* FROM ##TmepTable as Alias
IF OBJECT_ID('tempdb..##TmepTable') IS NOT NULL DROP TABLE ##TmepTable
Here is step by step solution for your problem.
Check for your temporary tables if they exist, and delete them.
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
IF OBJECT_ID('tempdb..##abc') IS NOT NULL
DROP TABLE ##abc
Store your main query result in first temp table (this step is for simplicity and more readability).
SELECT *
INTO #temp
FROM (SELECT ResourceKey, ResourceValue
FROM LocaleStringResources
where StateId ='+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =' + LTRIM(RTRIM(#CultureCode)) + ') AS S
Write below query to create your pivot and store result in another temp table.
DECLARE #str NVARCHAR(1000)
DECLARE #sql NVARCHAR(1000)
SELECT #str = COALESCE(#str+',', '') + ResourceKey FROM #temp
SET #sql = N'select * into ##abc from (select ' + #str + ' from (SELECT ResourceKey, ResourceValue FROM #temp) as A
Pivot
(
max(ResourceValue)
for ResourceKey in (' + #str + ')
)as pvt) as B'
Execute below query to get the pivot result in your next temp table ##abc.
EXECUTE sp_executesql #sql
And now you can use ##abc as table where-ever you want like
select * from ##abc
Hope this will help you.

error in creating a temp table using dynamic sql

declare #TableName nvarchar(max)
set #TableName='addresses'
DECLARE #sql NVARCHAR(MAX)
set #sql= 'create table #tempadd ( '
SELECT #sql=#sql + STUFF( -- Remove first comma
(
SELECT ', ' + column_name+' '+ case when DATA_TYPE='varchar' then DATA_TYPE +'(500)' else DATA_TYPE end FROM -- create comma separated values
(
SELECT column_name,DATA_TYPE FROM information_schema.columns where table_name = #TableName --Your query here
) AS T FOR XML PATH('')
)
,1,1,'')
set #sql =#sql+' ) '
print #sql
--SET #sql='SELECT * into #tempadd FROM '+#TableName+ ' WHERE 1=2'
EXEC sp_executesql #sql
select * from #tempadd
This results in an error:
Msg 208, Level 16, State 0, Line 25
Invalid object name '#tempadd'.
Your temp table is limited to the scope of your dynamic query since it is defined within.
You could add your select * from #tempadd statement to the end of your #sql query. Alternatively I think you can define #tempadd before your dynamic query and it should be accessible, but I'm not certain on that.
thanks to this blog
The problem here is the scope of the session. When we execute dynamic sql via EXEC or sp_executesql a new scope is created for a child session. Any objects created in that session are dropped as soon as the session is closed.
One solution I have found for this problem is creating the table in the "parent" scope and then just using dynamic sql to modify the table. For this to work a table is created with a minimum set of colums. And then we use the ALTER TABLE statement with dynamic SQL. The Child session has access to the objects created in the parent session so the table can be modified with dynamic sql:
DECLARE #SQL NVARCHAR(4000)
CREATE TABLE #Temp ( id int null)
SELECT #SQL = 'ALTER #Temp ADD Col1 int null'
EXEC (#SQL)
SELECT * FROM #Temp
DROP TABLE #Temp
This table is visible and both columns will show up.

using temp tables in SQL Azure

I am writing a query to pivoting table elements where column name is generated dynamically.
SET #query = N'SELECT STUDENT_ID, ROLL_NO, TITLE, STUDENT_NAME, EXAM_NAME, '+
#cols +
' INTO ##FINAL
FROM
(
SELECT *
FROM #AVERAGES
UNION
SELECT *
FROM #MARKS
UNION
SELECT *
FROM #GRACEMARKS
UNION
SELECT *
FROM #TOTAL
) p
PIVOT
(
MAX([MARKS])
FOR SUBJECT_ID IN
( '+
#cols +' )
) AS FINAL
ORDER BY STUDENT_ID ASC, DISPLAYORDER ASC, EXAM_NAME ASC;'
EXECUTE(#query)
select * from ##FINAL
This query works properly in my local database, but it doesn't work in SQL Azure since global temp tables are not allowed there.
Now if i change ##FINAL to #FINAL in my local database, but it gives me error as
Invalid object name '#FINAL' .
How can I resolve this issue?
Okay, after saying I didn't think it could be done, I might have a way. It's ugly though. Hopefully, you can play with the below sample and adapt it to your query (without having your schema and data, it's too tricky for me to attempt to write it):
declare #cols varchar(max)
set #cols = 'object_id,schema_id,parent_object_id'
--Create a temp table with the known columns
create table #Boris (
ID int IDENTITY(1,1) not null
)
--Alter the temp table to add the varying columns. Thankfully, they're all ints.
--for unknown types, varchar(max) may be more appropriate, and will hopefully convert
declare #tempcols varchar(max)
set #tempcols = #cols
while LEN(#tempcols) > 0
begin
declare #col varchar(max)
set #col = CASE WHEN CHARINDEX(',',#tempcols) > 0 THEN SUBSTRING(#tempcols,1,CHARINDEX(',',#tempcols)-1) ELSE #tempcols END
set #tempcols = CASE WHEN LEN(#col) = LEN(#tempcols) THEN '' ELSE SUBSTRING(#tempcols,LEN(#col)+2,10000000) END
declare #sql1 varchar(max)
set #sql1 = 'alter table #Boris add [' + #col + '] int null'
exec (#sql1)
end
declare #sql varchar(max)
set #sql = 'insert into #Boris (' + #cols + ') select ' + #cols + ' from sys.objects'
exec (#sql)
select * from #Boris
drop table #Boris
They key is to create the temp table in the outer scope, and then inner scopes (code running within EXEC statements) have access to the same temp table. The above worked on SQL Server 2008, but I don't have an Azure instance to play with, so not tested there.
If you create a temp table, it's visible from dynamic sql executed in your spid, if you create the table in dynamic sql, it's not visible outside of that.
There is a workaround. You can create a stub table and alter it in your dynamic sql. It requires a bit of string manipulation but I've used this technique to generate dynamic datasets for tsqlunit.
CREATE TABLE #t1
(
DummyCol int
)
EXEC(N'ALTER TABLE #t1 ADD foo INT')
EXEC ('insert into #t1(DummyCol, foo)
VALUES(1,2)')
EXEC ('ALTER TABLE #t1 DROP COLUMN DummyCol')
select *from #t1