SQL Server: Select from #variable_table_name (problem with nested variables) - sql

I need to select form a variable table name, which seems to not work in SQL Server (see example nr 2).
I tried making an intermediate step that creates a string with my variable table name as static text (see example nr3), but that failed too, probably because I have nested variables.
Can anyone help me fix it?
------------Example Nr 1--------------
-- Working query without variable Table
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = 'SELECT ';
SELECT #Sql = #Sql +'['+[Modell Pset (S1 Slab)]+']'+ ' AS ''' +''+[C3D Pset (S1 Slab)]+''', ' FROM [MTO_Psets_S1_Slab]
PRINT #sql
--Result of "print #sql": SELECT [c3d:abschnitt] AS 'Abschnitt', [ALL] AS 'Eigenschaft_1', [ALL] AS 'Eigenschaft_2', [Geschoss] AS 'Geschoss', [ALL] AS 'Ist_Aussen', [ALL] AS 'Material_1', [ALL] AS 'Material_2', [ALL] AS 'Objekt', [c3d:teilprojekt] AS 'Teilprojekt',
SET #Sql = (SELECT LEFT(#Sql, LEN(#Sql)-1));
SET #Sql = #Sql + ' FROM [modell_properties_slab]';
EXEC sp_executesql #Sql;
------------Example Nr 2--------------
-- When I replace table with a variable, the query doesn't work anymore
DECLARE #Sql NVARCHAR(MAX);
DECLARE #Table NVARCHAR(MAX);
SET #table = '[MTO_Psets_S1_Slab]'
SET #Sql = 'SELECT ';
SELECT #Sql = #Sql +'['+[Modell Pset (S1 Slab)]+']'+ ' AS ''' +''+[C3D Pset (S1 Slab)]+''', ' FROM #table
SET #Sql = (SELECT LEFT(#Sql, LEN(#Sql)-1));
SET #Sql = #Sql + ' FROM [modell_properties_slab]';
EXEC sp_executesql #Sql
-- Error message:
-- Msg 1087, Level 16, State 1, Line 24
-- Must declare the table variable "#Table".
------------Example Nr 3--------------
-- When I rewrite the query to have the table inside the apostrophes,
-- it doesn't work (compare the result of "print #sql" with the example nr1)
declare #slab nvarchar(MAX) ='slab'
DECLARE #intermediate_step nvarchar(MAX);
set #intermediate_step = '''[''[Modell Pset (S1 Slab)]+'']''+ '' AS '''''' +''''+[C3D Pset (S1 Slab)]+'''''', '' FROM [MTO_Psets_S1_'+#Slab+']'
print #intermediate_step
--Result of "print #intermediate_step": '['[Modell Pset (S1 Slab)]+']'+ ' AS ''' +''+[C3D Pset (S1 Slab)]+''', ' FROM [MTO_Psets_S1_slab]
DECLARE #Sql nvarchar(MAX);
SET #Sql = 'SELECT ';
SELECT #Sql = #Sql + #intermediate_step
print #sql
--Result of "print #sql": SELECT '['[Modell Pset (S1 Slab)]+']'+ ' AS ''' +''+[C3D Pset (S1 Slab)]+''', ' FROM [MTO_Psets_S1_slab]
SET #Sql = (SELECT LEFT(#Sql, LEN(#Sql)-1));
SET #Sql = #Sql + ' FROM [modell_properties_slab]';
EXEC sp_executesql #Sql;
I'll attach the tables i used in the query and the result table from the working query "example nr 1" as JPG: MTO_Psets_S1_Slab, modell_properties_slab, Result query
MTO_Psets_S1_Slab
modell_properties_slab
Result query

I rewrote the query like this and it works:
exec('
declare #t as table ([C3D Pset (S1 '+#slab+')] nvarchar (100), [Modell Pset (S1 '+#slab+')] nvarchar (100));
DECLARE #Sql nvarchar(MAX);
insert #t ([C3D Pset (S1 '+#slab+')] , [Modell Pset (S1 '+#slab+')] )
select * from [MTO_Psets_S1_'+#slab+']
SET #Sql = ''SELECT '';
SELECT #Sql = #Sql +''[''+[Modell Pset (S1 Slab)]+'']''+ '' AS '''''' +''''+[C3D Pset (S1 Slab)]+'''''', '' FROM #t
SET #Sql = (SELECT LEFT(#Sql, LEN(#Sql)-1));
SET #Sql = #Sql + '' FROM [modell_properties_'+#slab+']'';
EXEC sp_executesql #Sql
')

Related

Select from list of tables / multiple tables with nested query - MS SQL

Not sure if this has an easy answer, but I'm basically trying to do this:
select id
from (select table_name from information_schema.columns where column_name = 'id')
where id = 1234
So passing in a list of tables into the FROM.
I'm trying to check all tables where this column exists if they have a matching value for 1234.
And without typing 31 times, select from table1, select from table2, etc etc
And without doing a bunch of joins.
possible?
I wrote this stored procedure.
It SUCKS I know, I've never written anything in tsql (teesquill?) before, and my goal was just to get something that works.
// find all occurrences of value in DB
USE [your db here]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[usp_query] (
#value NVARCHAR(128),
#colName NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
-- DECLARE #tables NVARCHAR(MAX);
DECLARE #tabName NVARCHAR(MAX);
-- DECLARE #colName NVARCHAR(MAX);
DECLARE #count INT;
-- construct SQL
--SET #tables = N'SELECT Table_name FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME LIKE "yourcollumn"'
DECLARE tabs CURSOR FOR SELECT table_name FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME LIKE 'yourcollumn'
OPEN tabs
FETCH NEXT FROM tabs INTO #tabName
WHILE ##fetch_status = 0
BEGIN
--SET #count = N'SELECT count ( ' + #colName + ' ) FROM ' + #tabName + ' where ' + #colName + ' = ' + #value
DECLARE #SQLString NVARCHAR(500);
DECLARE #ParmDefinition NVARCHAR(500);
DECLARE #varOut NVARCHAR(25);
SET #SQLString = N'SELECT #count = count ( ' + #colName + ' ) FROM ' + #tabName + ' where ' + #colName + ' = ' + #value;
SET #ParmDefinition = N'#count NVARCHAR(25) OUTPUT';
EXECUTE sp_executesql #SQLString
,#ParmDefinition
,#count = #varOut OUTPUT;
SET #sql = N'SELECT ' + #colName + ' FROM ' + #tabName + ' where yourcollumn = ' + #value;
IF #varOut > 0
SELECT #tabName AS 'value found in table'
--EXEC sp_executesql #sql
FETCH NEXT FROM tabs INTO #tabName
END
CLOSE tabs
END;

Make multiple columns into one column in sql

I have a table Employee which have several fields like FirstName,LastName,Email,....... . So what i want to do is that in my case selection of column is dynamic
Declare #columnNeeded nvarchar(max)
Example one
Set #columnNeeded = 'FirstName,Email'
Select #columnNeeded from Employee
Example Two
Set #columnNeeded = 'FirstName,LastName'
Select #columnNeeded from Employee
This is pretty simple , now what i want is that regardless of what column will be in result set i need all column selected in one column as comma seperated string . I saw Group_Concat() in mysql but don't know how to do this in sql .So is this possible ?
You can do this with dynamic SQL:
declare #sql nvarchar(max) = 'select #columns from Employee';
declare #columnNeeded nvarchar(max) = 'FirstName,Email';
set #sql = replace(#sql, '#columns', #columnNeeded);
exec sp_executesql #sql;
EDIT:
If you want them as one column, you could do:
declare #sql nvarchar(max) = 'select #columns from Employee';
declare #columnNeeded nvarchar(max) = 'FirstName,Email';
set #sql = replace(replace(#sql, '#columns', #columnNeeded), ',', '+'',''+');
exec sp_executesql #sql;
To type-safe you would cast the column values:
declare #tmp nvarchar(4000) = 'cast(' +
replace(#columnNeeded, ',', ', nvarchar(4000)), cast(') +
', nvarchar(4000))'
set #sql = replace(replace(#sql, '#columns', #columnNeeded), ',', '+'',''+');
If this works as expected, it adds cast(<col> as nvarchar(4000)) to each of the columns in the list.
You have to use Dynamic SQL. Since you have different Data types in your table you may have to convert the columns to Varchar to concatenate the result into single column.
DECLARE #sql NVARCHAR(max),
#cols NVARCHAR(max) ='FirstName,Email'
SELECT #cols = 'convert(varchar(100),'
+ Replace(#cols+')+', ',', ')+'',''+convert(varchar(100),')
SELECT #cols = LEFT(#cols, Len(#cols) - 1)
SET #sql ='select ' + #cols + ' from Employee '
--print #sql
EXEC Sp_executesql #sql;
Working Example :
CREATE TABLE #test1([Key] INT,ID INT,Value VARCHAR(2))
INSERT #test1
VALUES (1,1,'C' ),(2,1,'C' ),(3,1,'I' )
DECLARE #sql NVARCHAR(max),
#cols NVARCHAR(max) ='ID,Value'
SELECT #cols = 'convert(varchar(100),'
+ Replace(#cols+')+', ',', ')+'',''+convert(varchar(100),')
SELECT #cols = LEFT(#cols, Len(#cols) - 1)
SET #sql ='select ' + #cols + ' from #test1 '
EXEC Sp_executesql #sql;

Union of multiple sp_MSforeachdb result sets

I can successfully query the same table in multiple databases as follows:
DECLARE #command varchar(1000)
SELECT #command = 'select * from table'
EXEC sp_MSforeachdb #command
However, all of these results are, as expected, returned in different result windows. What's the easiest way to perform a union of all of these results?
Please stop using sp_MSforeachdb. For anything. Seriously. It's undocumented, unsupported, and spectacularly broken:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
If you know that all databases have the same table (and that they all have the same structure!), you can do this:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'UNION ALL SELECT col1,col2 /*, etc. */
FROM ' + QUOTENAME(name) + '.dbo.tablename'
FROM sys.databases WHERE database_id > 4 AND state = 0;
SET #sql = STUFF(#sql, 1, 10, '');
EXEC sp_executesql #sql;
This ignores system databases and doesn't attempt to access any databases that are currently not ONLINE.
Now, you may want to filter this further, e.g. not include any databases that don't have a table called tablename. You'll need to nest dynamic SQL in this case, e.g.:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'DECLARE #cmd NVARCHAR(MAX);
SET #cmd = N'''';';
SELECT #sql = #sql + N'
SELECT #cmd = #cmd + N''UNION ALL
SELECT col1,col2 /*, etc. */ FROM '
+ QUOTENAME(name) + '.dbo.tablename ''
WHERE EXISTS (SELECT 1 FROM ' + QUOTENAME(name)
+ '.sys.tables AS t
INNER JOIN ' + QUOTENAME(name) + '.sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE t.name = N''tablename''
AND s.name = N''dbo'');'
FROM sys.databases WHERE database_id > 4 AND state = 0;
SET #sql = #sql + N';
SET #cmd = STUFF(#cmd, 1, 10, '''');
PRINT #cmd;
--EXEC sp_executesql #cmd;';
PRINT #sql;
EXEC sp_executesql #sql;
This doesn't validate the column structure is compatible, but you'll find that out pretty quickly.
Another way to skin this cat is to use dynamic SQL:
DECLARE #sql varchar(max);
SELECT #sql = Coalesce(#sql + ' UNION ALL ', '') + 'SELECT list, of, columns FROM ' + QuoteName(name) + '.schema.table'
FROM sys.databases
;
PRINT #sql
--EXEC (#sql);
Had some collation issues and had to use
AND COLLATION_NAME = 'SQL_Latin1_General_CP1_CI_AS'

Dynamic SQL using Sp_executesql

I have a stored procedure which I have to rewrite using sp_executesql. I want to use sp_executesql instead of exec because of performance issue on my SQL Server instance.
Here is my code:
ALTER PROCEDURE [dbo].[sp_TestSp1]
#Type1 VARCHAR(256)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sqlCommand varchar(2000) = ''
DECLARE #columnList varchar(1000) = ''
DECLARE #dynamicSql varchar(1000) = ''
IF (#Type1 IS NOT NULL )
BEGIN
SET #dynamicSql = #dynamicSql + 'AND tbl1.DTypeID IN ( '+ #Type1+ ' )'
END
SET #columnList = 'SELECT DISTINCT tbl2.ID, Name AS PName '
SET #sqlCommand = #columnList
+ ' FROM tbl1 '
+ ' INNER JOIN tbl3 ON tbl1.NID= tbl3.NID '
+ ' INNER JOIN tbl4 ON tbl3.DID = tbl4.DID '
+ ' WHERE TT=1 AND IsActive=1 AND tbl1.DTypeID IN (1,3,5)'
+ #dynamicSql
EXEC (#sqlCommand)
Replace your EXEC (#sqlCommand) statement with the following.
EXECUTE sp_executesql #sqlCommand
TechNet Link

Temporary tables in Stored procedure are not accesing data?

This is My code
create proc TEMP
AS
BEGIN
DECLARE #SQL nvarchar(4000)
IF OBJECT_ID(N'tempdb..#TEMP1') IS NOT NULL
DROP TABLE #TEMP1;
SET #SQL ='SELECT CUSTOMERS,AREA,HOUSEHOLDS'+CHAR(10)
SET #SQL = #SQL +'INTO #TEMP1'+CHAR(10)
SET #SQL = #SQL +'FROM NEW'+CHAR(10)
PRINT(#SQL)
EXEC (#SQL)
IF OBJECT_ID(N'tempdb..#TEMP2') IS NOT NULL
DROP TABLE #TEMP2;
SET #SQL ='SELECT CUSTOMERS,AREA,VEHICELS'+CHAR(10)
SET #SQL = #SQL +'INTO #TEMP2'+CHAR(10)
SET #SQL = #SQL +'FROM OLD'+CHAR(10)
PRINT(#SQL)
EXEC (#SQL)
IF OBJECT_ID(N'tempdb..#TEMP3') IS NOT NULL
DROP TABLE #TEMP3;
SET #SQL ='SELECT 0.VEHICELS,C.HOUSEHOLDS'+CHAR(10)
SET #SQL = #SQL +'INTO #TEMP3'+CHAR(10)
SET #SQL = #SQL +'FROM #TEMP1 C'+CHAR(10)
SET #SQL = #SQL +'INNER JOIN #TEMP2 O '+CHAR(10)
SET #SQL = #SQL +'on C.CUSTOMERS=O.CUSTOMERS'+CHAR(10)
SET #SQL = #SQL +'AND C.AREA=O.AREA'+CHAR(10)
PRINT(#SQL)
EXEC (#SQL)
END
My error:
Msg 208, Level 16, State 0, Line 1
Invalid object name '#TEMP1'.
First TEMP1 and TEMP2 are working well, but when it comes to TEMP3 it doesn't access TEMP1 where the data is present
Can you guys check this confusing problem ???
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE ##T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO ##T1 (Col1) VALUES ('This will work.')
SELECT * FROM ##T1
DO WE GET ANY PROBLEMS IN FUTURE IF WE USE GLOBALTEMPORARY TABLES???
once the exec finishes executing the #temp table is deleted
maybe if you try using a wider scope, it could succeed, I don't know.
try something like this.
create proc TEMP
AS
BEGIN
DECLARE #now datetime;
DECLARE #TempTableTableSuffix sysname
SET #now = GETDATE()
select #TempTableTableSuffix = CONVERT(VARCHAR, CONVERT(int,RAND(DATEPART(MILLISECOND,#now)+1000*(DATEPART(SECOND,#now)+60*(DATEPART(MINUTE,#now)+60*DATEPART(HOUR,#now)))) * 100000000))
DECLARE #SQL nvarchar(4000)
SET #SQL ='SELECT CUSTOMERS,AREA,HOUSEHOLDS'+CHAR(10)
SET #SQL = #SQL +'INTO ##TEMP1' + #TempTableTableSuffix + CHAR(10)
SET #SQL = #SQL +'FROM NEW'+CHAR(10)
PRINT(#SQL)
EXEC (#SQL)
SET #SQL ='SELECT CUSTOMERS,AREA,VEHICELS'+CHAR(10)
SET #SQL = #SQL +'INTO ##TEMP2' + #TempTableTableSuffix + CHAR(10)
SET #SQL = #SQL +'FROM OLD'+CHAR(10)
PRINT(#SQL)
EXEC (#SQL)
SET #SQL ='SELECT 0.VEHICELS,C.HOUSEHOLDS'+CHAR(10)
SET #SQL = #SQL +'INTO ##TEMP3'+CHAR(10)
SET #SQL = #SQL +'FROM ##TEMP1' + + #TempTableTableSuffix + ' C'+CHAR(10)
SET #SQL = #SQL +'INNER JOIN #TEMP2'+ #TempTableTableSuffix + ' O '+CHAR(10)
SET #SQL = #SQL +'on C.CUSTOMERS=O.CUSTOMERS'+CHAR(10)
SET #SQL = #SQL +'AND C.AREA=O.AREA'+CHAR(10)
PRINT(#SQL)
EXEC (#SQL)
END
anyway, I think you don't need dynamic SQL for this.
Use single EXEC -
DECLARE #SQL nvarchar(MAX)
IF OBJECT_ID(N'tempdb..#TEMP1') IS NOT NULL
DROP TABLE #TEMP1;
IF OBJECT_ID(N'tempdb..#TEMP2') IS NOT NULL
DROP TABLE #TEMP2;
IF OBJECT_ID(N'tempdb..#TEMP3') IS NOT NULL
DROP TABLE #TEMP3;
SET #SQL = ' SELECT CUSTOMERS,AREA,HOUSEHOLDS'+CHAR(10)
SET #SQL = #SQL + ' INTO #TEMP1'+CHAR(10)
SET #SQL = #SQL + ' FROM NEW'+CHAR(10)
SET #SQL = #SQL + ' SELECT CUSTOMERS,AREA,VEHICELS'+CHAR(10)
SET #SQL = #SQL + ' INTO #TEMP2'+CHAR(10)
SET #SQL = #SQL + ' FROM OLD'+CHAR(10)
SET #SQL = #SQL + ' SELECT 0.VEHICELS,C.HOUSEHOLDS'+CHAR(10)
SET #SQL = #SQL + ' INTO #TEMP3'+CHAR(10)
SET #SQL = #SQL + ' FROM #TEMP1 C'+CHAR(10)
SET #SQL = #SQL + ' INNER JOIN #TEMP2 O '+CHAR(10)
SET #SQL = #SQL + ' on C.CUSTOMERS=O.CUSTOMERS'+CHAR(10)
SET #SQL = #SQL + ' AND C.AREA=O.AREA'+CHAR(10)
PRINT(#SQL)
EXEC (#SQL)