I have several tables with common table names. I want to select all data from them without specifying all tables and union them. I'm thinking of using information_schema.tables to accomplish this.
Tables:
tbl_20160201
tbl_20160202
tbl_20160203
tbl_20160204
tbl_20160205
Query:
SELECT *
FROM (
SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'tbl_201602%'
) a
However, this query only returns the table names and not the data in the tables. I need to union all the tables included in the query. Thank you.
Assuming these tables have the same number of columns and data types, you can use dynamic sql:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql = #sql +
'SELECT * FROM ' + TABLE_NAME + ' UNION ALL' + CHAR(10)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'tbl_201602%';
SELECT #sql = SUBSTRING(#sql,1, LEN(#sql) - 11);
PRINT(#sql)
EXEC (#sql)
Otherwise, you'll get an error saying:
All queries combined using a UNION, INTERSECT or EXCEPT operator must
have an equal number of expressions in their target lists
Below query might help you :-
DECLARE #SQL VARCHAR(max)
DECLARE #T as sysname
-- Make sure below temporary table structure same as the tables e.g tbl_201602 which you want to union
declare #TT as Table(
Col1 [decimal](18, 2) NOT NULL,
Col2 [date] NOT NULL,
Col3 [varchar](200) NOT NULL
)
DECLARE TName CURSOR FORWARD_ONLY STATIC FOR
select TABLE_NAME
from
information_schema.tables where TABLE_NAME like 'tbl_201602%'
OPEN TName
FETCH NEXT FROM TName INTO #T
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #SQL = 'select * from' + ' ' + #T
INSERT INTO #TT
EXEC(#SQL)
FETCH NEXT FROM TName INTO #T
END
CLOSE TName
DEALLOCATE TName
select * from #TT
This will give the union of all tables in a single temporary table #TT
NOTE : Just make sure that all comman tables are having same no of columns and in same ordering, otherwise it may raise error
Related
I'm duplicating a series of tables using a loop in SQL. I've inferred from SQL Server Maximum rows that can be inserted in a single insert statment that the maximum rows which can be inserted using a select * is 10,000 for my SQL SERVER. However, the source tables are bigger than 10,000 rows. Is there a way to bypass this limitation without explicitly stating the columns in the select statement, since these will be different for each table?
DECLARE #iTable TABLE (TableName VARCHAR(50),Id int identity(1,1))
INSERT INTO #iTable
Select distinct table_name From INFORMATION_SCHEMA.COLUMNS
Where table_name like 'D0%' OR table_name like 'D1%'
DECLARE #imax int
DECLARE #iSQL VARCHAR(MAX)
DECLARE #iTableName VARCHAR(max)
DECLARE #iid int = 1
select #imax = MAX(Id) from #iTable
WHILE (#iid <= #imax)
BEGIN
SELECT #iTableName = TableName FROM #iTable WHERE Id = #iid
SET #iSQL = 'select * into st_'+ #iTableName +' from '+ #iTableName +';'
EXEC(#iSQL)
SET #iid = #iid +1
END
As already mentioned there is no limitation of the number of rows when using select into. However, you could certainly simplify your code and get rid of that loop.
DECLARE #iSQL NVARCHAR(MAX) = ''
SELECT #iSQL = #iSQL + 'select * into st_'+ TABLE_NAME +' from '+ TABLE_NAME +';'
from INFORMATION_SCHEMA.COLUMNS
Where TABLE_NAME like 'D0%'
OR TABLE_NAME like 'D1%'
group by TABLE_NAME
select #iSQL
--uncomment below when you are satisfied the dynamic sql is correct.
--exec sp_executesql #iSQL
I am looking to return a view name and all record ID's where billingAddress != shippingAddress to further review. I need to query all views in one database. This is my thought process and if their is a better way or faster way to write the query by all means help me out!
What I am stuck on is how to return the view name with the recordID?
Create Table #T (ID Int Identity Not Null, ViewNames VARCHAR(1000)
Create Table #2 (viewNames varchar(1000))
Insert Into #T (ViewNames)
Select '['+C.Table_Catalog+'].['+C.Table_Schema+'].['+C.Table_Name+']' TableName
FROM Information_Schema.Columns c
Join information_schema.Tables T on C.Table_Catalog = T.Table_Catalog
AND C.Table_Schema = T.Table_Schema
AND C.Table_Name = T.Table_Name
Where T.Table_Type = 'View'
Group By '['+C.Table_Catalog+'].['+C.Table_Schema+'].['+C.Table_Name+']'
---Now this is the piece that I am stuck on as I do not know how to insert the view name into the table as well on each iteration
Declare #N int, #Str nvarchar(2000), #viewname nvarchar(2000), #MaxID int
Set #N = 1
Select #MaxID = Max(ID)
From #T
While (#N<#MaxID)
Begin
Select #viewname= viewname
From #T
Set #Str = ' Insert Into #2(viewname)
Select Top 1 '''+#viewname+'''
From '+#viewname+'
where exists(Select recordID from '+#viewname+' where [shipaddress] != [billaddress] ) '
Exec sp_eecutesql #str
Set #N = #N + 1
End
Select * from #t
Try changing your dynamic query like this.
You said you wanted the view name, and record id, so you need to add a column to #2
SET #Str = 'INSERT INTO #2(viewname, recordid)
SELECT ''' + quotename(#viewname) + ''', recordID
FROM '+ quotename(#viewname) + '
WHERE [shipaddress] != [billaddress]'
EXEC sp_executesql #str
Unless you're sure of the object names, you should try and use quotename when building up dynamic SQL
You do have a problem in your logic though...
You are missing a where clause in the query that assigns the value to #viewname
Try this...
SELECT #viewname= viewname
FROM #T
WHERE ID = #N
I do not understand sql returns set of rows so your variable #viewname can not be assigned value row by row. By default your #viewname will be assigned last row of table T.
I need to select from table where the the table name suffix are from another table, like this :
declare #value nvarchar(3),
#table nvarchar(1000),
#SQLST NVARCHAR(255);
set #value = N'select column1 from tableX';
EXEC #value
set #table ='partoftableY'
Set #SQLST ='select * from' +#tabel + #value -- here I create the table name
However there are multiple values in TableX (0-999) and so this doesn't work. Do I need a For Each type construct.
here in an example I created with two tables (partoftableY1 & partoftableY2) with different data in each
/*
create table tableX (column1 int);
insert into tablex
select 1
union all select 2;
create table partoftableY1 (data nvarchar(50));
create table partoftableY2 (data nvarchar(50));
insert into partoftableY1 select 'hey 1 here';
insert into partoftableY2 select 'hey 2 here';
*/
declare #sql nvarchar(max)
-- use the ability of SQL to build up string of all the sql you need to run
set #sql = 'select data from (select '''' as data'
select #sql = COALESCE(#sql + ' union all ', '')
+ 'select data from partoftableY'
+ cast(column1 as nvarchar(4)) from tableX
select #sql = #sql + ') X where data <>'''''
-- DEBUG for seeing what SQL you created
print #sql
-- Now execute the SQL
exec sp_executesql #sql= #sql
which gives me the results of
hey 1 here
hey 2 here
You will need to adjust it for types of your data, but this should give you the main idea
For reference here is the sql that was created and executed:
select data
from (
select '' as data
union all select data from partoftableY1
union all select data from partoftableY2
) X
where data <>''
N.B.
I put formatted it for easier reading, as it's actually created as one long line
I used selet data and not select * as the number of columns needs to be the same for each select in the union. You will need to select the columns you need and then make changes ensure that all the columns in the selects in the union are the same.
There is a dummy select at the top of the union to make the union code easy - no conditionals needed as whether the union all needs to present
I used the out select over the whole union to enable you to get sid of the dummy select
You can try this
DECLARE #SQLST NVARCHAR(max)='';
DECLARE #select nvarchar(max)=N'select * from partoftableY'
DECLARE #union nvarchar(max)=N'
UNION ALL
'
SELECT #SQLST=#select+column1+#union
FROM tablex
SELECT #SQLST=substring(#SQLST,1,LEN(#SQLST)-11)
EXEC sp_executesql #SQLST
My code:
SELECT * INTO #t FROM CTABLE WHERE CID = #cid --get data, put into a temp table
ALTER TABLE #t
DROP COLUMN CID -- remove primary key column CID
INSERT INTO CTABLE SELECT * FROM #t -- insert record to table
DROP TABLE #t -- drop temp table
The error is:
Msg 8101,
An explicit value for the identity column in table 'CTABLE' can only
be specified when a column list is used and IDENTITY_INSERT is ON.
And I did set
SET IDENTITY_INSERT CTABLE OFF
GO
DECLARE
#cid INT,
#o INT,
#t NVARCHAR(255),
#c NVARCHAR(MAX),
#sql NVARCHAR(MAX);
SELECT
#cid = 10,
#t = N'dbo.CTABLE',
#o = OBJECT_ID(#t);
SELECT #c = STRING_AGG(QUOTENAME(name), ',')
FROM sys.columns
WHERE [object_id] = #o
AND is_identity = 0;
SET #sql = 'SELECT ' + #c + ' INTO #t
FROM ' + #t + ' WHERE CID = #cid;
INSERT ' + #t + '('+ #c + ')
SELECT ' + #c + ' FROM #t;'
PRINT #sql;
-- exec sp_executeSQL #sql,
-- N'#cid int',
-- #cid = #cid;
However it seems much easier to just build the following SQL and avoid the #temp table altogether:
SET #sql = 'INSERT ' + #t + '(' + #c + ')
SELECT ' + #c + ' FROM ' + #t + '
WHERE CID = #cid;';
PRINT #sql;
-- exec sp_executeSQL #sql,
-- N'#cid int',
-- #cid = #cid;
Try this:
SELECT * INTO #t FROM CTABLE WHERE CID = #cid
ALTER TABLE #t
DROP COLUMN CID
INSERT CTABLE --Notice that INTO is removed here.
SELECT top(1) * FROM #t
DROP TABLE #t
Test Script(Tested in SQL 2005):
CREATE TABLE #TestIDNT
(
ID INT IDENTITY(1,1) PRIMARY KEY,
TITLE VARCHAR(20)
)
INSERT #TestIDNT
SELECT 'Cybenate'
Try specifying the columns:
INSERT INTO CTABLE
(col2, col3, col4)
SELECT col2, col3, col4
FROM #t
Seems like it might be thinking you are trying to insert into the PK field since you are not explicitly defining the columns to insert into. If Identity insert is off and you specify the non-pk columns then you shouldn't get that error.
Here's an example to dynamically build a list of columns - excluding the primary key columns - and execute the INSERT
declare #tablename nvarchar(100), #column nvarchar(100), #cid int, #sql nvarchar(max)
set #tablename = N'ctable'
set #cid = 1
set #sql = N''
declare example cursor for
select column_name
from information_schema.columns
where table_name = #tablename
and column_name not in (
select column_name
from information_schema.key_column_usage
where constraint_name in (select constraint_name from information_schema.table_constraints)
and table_name = #tablename
)
open example
fetch next from example into #column
while ##fetch_status = 0
begin
set #sql = #sql + N'[' + #column + N'],'
fetch next from example into #column
end
set #sql = substring(#sql, 1, len(#sql)-1)
close example
deallocate example
set #sql = N'insert into ' + #tablename + '(' + #sql + N') select top(1) ' + #sql + ' from #t'
--select #sql
exec sp_executesql #sql
If using SQL Server Management Studio and your problems you have too many fields to type them all out except the identity column, then right click on the table and click "Script table as" / "Select To" / "New Query Window".
This will provide a list of fields that you can copy & paste into your own query and then just remove the identity column.
Try invoking the INSERT statement with EXEC:
SELECT * INTO #t FROM CTABLE WHERE CID = #cid
ALTER TABLE #t
DROP COLUMN CID
EXEC('INSERT INTO CTABLE SELECT top(1) * FROM #t')
DROP TABLE #t
You can't do this:
INSERT INTO CTABLE SELECT top(1) * FROM #t
Because the column listings aren't the same. You've dropped the PK column from #t, so you have 1 less column in #t than in CTABLE. This is the equivalent of the following:
INSERT INTO CTABLE(pk, col1, col2, col3, ...)
select top(1) col1, col2, col3, ...
from #t
This wouldn't work for obvious reasons. Similarly, you aren't going to be able to specify the * wildcard to do the insert if you're not inserting all of the columns. The only way to do the insert without including the PK is to specify every column. You can generate a list of columns through dynamic sql, but you'll have to specify them one way or another.
The code is as follows:
ALTER PROCEDURE dbo.pdpd_DynamicCall
#SQLString varchar(4096) = null
AS
Begin
create TABLE #T1 ( column_1 varchar(10) , column_2 varchar(100) )
insert into #T1
execute ('execute ' + #SQLString )
select * from #T1
End
The problem is that I want to call different procedures that can give back different columns.
Therefore I would have to define the table #T1 generically.
But I don't know how.
Can anyone help me on this problem?
Try:
SELECT into #T1 execute ('execute ' + #SQLString )
And this smells real bad like an sql injection vulnerability.
correction (per #CarpeDiem's comment):
INSERT into #T1 execute ('execute ' + #SQLString )
also, omit the 'execute' if the sql string is something other than a procedure
You can define a table dynamically just as you are inserting into it dynamically, but the problem is with the scope of temp tables. For example, this code:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE #T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO #T1 (Col1) VALUES ('This will not work.')
SELECT * FROM #T1
will return with the error "Invalid object name '#T1'." This is because the temp table #T1 is created at a "lower level" than the block of executing code. In order to fix, use a global temp table:
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
Hope this helps,
Jesse
Be careful of a global temp table solution as this may fail if two users use the same routine at the same time as a global temp table can be seen by all users...
create a global temp table with a GUID in the name dynamically. Then you can work with it in your code, via dyn sql, without worry that another process calling same sproc will use it. This is useful when you dont know what to expect from the underlying selected table each time it runs so you cannot created a temp table explicitly beforehand. ie - you need to use SELECT * INTO syntax
DECLARE #TmpGlobalTable varchar(255) = 'SomeText_' + convert(varchar(36),NEWID())
-- select #TmpGlobalTable
-- build query
SET #Sql =
'SELECT * INTO [##' + #TmpGlobalTable + '] FROM SomeTable'
EXEC (#Sql)
EXEC ('SELECT * FROM [##' + #TmpGlobalTable + '] ')
EXEC ('DROP TABLE [##' + #TmpGlobalTable + ']')
PRINT 'Dropped Table ' + #TmpGlobalTable
INSERT INTO #TempTable
EXEC(#SelectStatement)
Try Below code for creating temp table dynamically from Stored Procedure Output using T-SQL
declare #ExecutionName varchar(1000) = 'exec [spname] param1,param2 '
declare #sqlStr varchar(max) = ''
declare #tempTableDef nvarchar(max) =
(
SELECT distinct
STUFF(
(
SELECT ','+a.[name]+' '+[system_type_name]
+'
' AS [text()]
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) a
ORDER BY a.column_ordinal
FOR XML PATH ('')
), 1, 1, '') tempTableDef
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) b
)
IF ISNULL(#tempTableDef ,'') = '' RAISERROR( 'Invalid SP Configuration. At least one column is required in Select list of SP output.',16,1) ;
set #tempTableDef='CREATE TABLE #ResultDef
(
' + REPLACE(#tempTableDef,'
','') +'
)
INSERT INTO #ResultDef
' + #ExecutionName
Select #sqlStr = #tempTableDef +' Select * from #ResultDef '
exec(#sqlStr)
DECLARE #EmpGroup INT =3 ,
#IsActive BIT=1
DECLARE #tblEmpMaster AS TABLE
(EmpCode VARCHAR(20),EmpName VARCHAR(50),EmpAddress VARCHAR(500))
INSERT INTO #tblEmpMaster EXECUTE SPGetEmpList #EmpGroup,#IsActive
SELECT * FROM #tblEmpMaster
CREATE PROCEDURE dbo.pdpd_DynamicCall
AS
DECLARE #SQLString_2 NVARCHAR(4000)
SET NOCOUNT ON
Begin
--- Create global temp table
CREATE TABLE ##T1 ( column_1 varchar(10) , column_2 varchar(100) )
SELECT #SQLString_2 = 'INSERT INTO ##T1( column_1, column_2) SELECT column_1 = "123", column_2 = "MUHAMMAD IMRON"'
SELECT #SQLString_2 = REPLACE(#SQLString_2, '"', '''')
EXEC SP_EXECUTESQL #SQLString_2
--- Test Display records
SELECT * FROM ##T1
--- Drop global temp table
IF OBJECT_ID('tempdb..##T1','u') IS NOT NULL
DROP TABLE ##T1
End
Not sure if I understand well, but maybe you could form the CREATE statement inside a string, then execute that String? That way you could add as many columns as you want.