To check whether the table exists or not dynamically - sql-server

DECLARE #DROP VARCHAR(MAX)
DECLARE #TAB VARCHAR(MAX)='Employees'
DECLARE #CREATE VARCHAR(MAX)
DECLARE #SELECT VARCHAR(MAX)
--IF OBJECT_ID('DBO.'+#TAB,'U') IS NOT NULL
--BEGIN
IF (EXISTS (SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = #TAB))
BEGIN
SET #DROP = N'DROP TABLE '+#TAB
EXEC(#TAB)
END
SET #CREATE= 'CREATE TABLE '+#TAB+
'(
ID INT
,NAME VARCHAR(50)
,PHONE VARCHAR(25)
,ADDRESS VARCHAR(100)
)'
EXEC(#CREATE)
SET #SELECT='SELECT * FROM '+#TAB
EXEC(#SELECT)
I get an error, why?
Msg 2809, Level 16, State 1, Line 1
The request for procedure 'Employees' failed because 'Employees' is a table object.

You did only one mistake instead of using EXEC(#TAB) You should use EXEC(#DROP)
DECLARE #DROP VARCHAR(MAX)
DECLARE #TAB VARCHAR(MAX)='Employees'
DECLARE #CREATE VARCHAR(MAX)
DECLARE #SELECT VARCHAR(MAX)
--IF OBJECT_ID('DBO.'+#TAB,'U') IS NOT NULL
--BEGIN
IF
(EXISTS
(
SELECT
1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = #TAB
)
)
BEGIN
SET #DROP=N'DROP TABLE '+#TAB
print #DROP
EXEC(#DROP)
END
SET #CREATE=
'CREATE TABLE '+#TAB+
'(
ID INT
,NAME VARCHAR(50)
,PHONE VARCHAR(25)
,ADDRESS VARCHAR(100)
)'
EXEC(#CREATE)
SET #SELECT='SELECT * FROM '+#TAB
EXEC(#SELECT)

From SQL Server 2016 SP1 onwards, objects can DIE - Drop If Exists. So, for your case, you can skip the existence check, and in the dynamic SQL to build only the following T-SQL statement:
...
'DROP TABLE IF EXISTS ' + #TAB + ';' +
'CREATE TABLE ' + #TAB +
...
Literally every object can DIE, you can get more details here:

Related

Have this problem with calling stored procedure unknown object type

I am trying to write a query that needs to call a stored procedure. But it always throws an error:
Unknown object type 'TABLEIXICHistoricalData' used in a CREATE, DROP, or ALTER statement.
This is query:
USE ETLCourse
DECLARE #LOOP TABLE
(
ID INT IDENTITY(1,1),
TableName NVARCHAR(100)
)
INSERT INTO #LOOP (TableName)
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '%_Stocks%'
DECLARE #b INT = 1, #m INT, #t NVARCHAR(100)
SELECT #m = MAX(ID) FROM #LOOP
WHILE #b <= #m
BEGIN
SELECT #t = TableName
FROM #LOOP
WHERE ID = #b
EXECUTE [dbo].[stp_BuildNormalizedTable] #t
SET #b = #b + 1
END
and here is the procedure:
ALTER PROCEDURE [dbo].[stp_BuildNormalizedTable]
#table NVARCHAR(100)
AS
BEGIN
DECLARE #cleanTable NVARCHAR(100),
#s NVARCHAR(MAX)
SET #cleanTable = REPLACE(#table, '_Stocks', 'HistoricalData')
SET #s = 'CREATE TABLE' + #cleanTable + '(ID INT IDENTITY(1,1), Price DECIMAL(13, 4), PriceDate DATE)
INSERT INTO' + #cleanTable + '(Price,PriceDate) SELECT [Adj Close],[Date] FROM'
+ #table + ' ORDER BY Date ASC'
--PRINT #s
EXECUTE sp_executesql #s
END
It should copy two specific column and create a new table by using #Loop table and procedure
You need to add 'space' after 'create table' and 'insert into' and 'from'
declare #s nvarchar(max)
declare #cleantable nvarchar(100)
declare #table nvarchar(100)
set #cleantable = 'aaa'
set #table = 'bbb'
SET #s = 'CREATE TABLE' + #cleanTable + '(ID INT IDENTITY(1,1),Price Decimal(13,4),PriceDate DATE)
Insert into' + #cleanTable
+ '(Price,PriceDate) SELECT [Adj Close],[Date] FROM'
+ #table + ' ORDER BY Date ASC'
print #s
Output:
CREATE TABLEaaa(ID INT IDENTITY(1,1),Price Decimal(13,4),PriceDate DATE)
Insert intoaaa(Price,PriceDate) SELECT [Adj Close],[Date] FROMbbb ORDER BY Date ASC
Use 'print' to check your query.

SQL Server query to select all columns of a certain type and also show its max values

I am using SQL Server 2012.
The first part of my query is already answered in this thread. But I also want a second column that will show the corresponding maximum value of that column in its corresponding table.
I have tried this approach: use a function that takes in table name and column name as parameter and return the max value. But it is illegal to use dynamic SQL from a function. Moreover, i cannot seem to call a function from within a SELECT query.
I have also tried using stored procedure, but i cannot figure out how to call it and use it. Please suggest alternative ways to achieve this.
I am new to SQL Server.
Thanks
I think the easiest solution would be stored procedure. As far as I know:
Dynamic SQL can't be placed in functions
Dynamic SQL can't be place in OPENROWSET
I addition, if you write such procedure:
Beware of names containing spaces, qoutes (SQL injection possible)
MAX(column) on non-Indexed columns would require full scan (can be very slow)
Table and column names can be duplicated (placed in differend schemas)
Id duplicates and performance is not a problem, take a look at the following snippet:
CREATE PROC FindMaxColumnValues
#type sysname = '%',
#table sysname = '%'
AS
DECLARE #result TABLE (TableName sysname, ColumnName sysname, MaxValue NVARCHAR(MAX))
DECLARE #tab sysname
DECLARE #col sysname
DECLARE cur CURSOR FOR
SELECT TABLE_NAME TableName, COLUMN_NAME [Column Name]
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE LIKE #type and TABLE_NAME LIKE #table
OPEN cur
FETCH NEXT FROM cur INTO #tab, #col
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #sql nvarchar(MAX) = 'SELECT '+QUOTENAME(#tab,'''')+' [TableName], '+QUOTENAME(#col, '''')+' [ColumnName], MAX('+QUOTENAME(#col)+') FROM '+QUOTENAME(#tab)
INSERT INTO #result EXEC(#sql)
FETCH NEXT FROM cur INTO #tab, #col
END
CLOSE cur
DEALLOCATE cur
SELECT * FROM #result
Samples:
--MAX of INT's
EXEC FindMaxColumnValues 'INT'
--MAX of INT's in tables matching 'TestTab%'
EXEC FindMaxColumnValues 'INT', 'TestTab%'
--MAX of ALL columns
EXEC FindMaxColumnValues
Results:
TableName ColumnName MaxValue
IdNameTest ID 2
TestTable ID 5
TestTable Number 3
TableName ColumnName MaxValue
TestTable ID 5
TestTable Number 3
TableName ColumnName MaxValue
UpdateHistory UpdateTime 2016-07-14 12:21:37.00
IdNameTest ID 2
IdNameTest Name T2
TestTable ID 5
TestTable Name F
TestTable Number 3
You can use the below SP and enhance it per your Need,
CRETE PROCEDURE Getmaxtablecolval
AS
BEGIN
CREATE TABLE #t
(
tablename VARCHAR(50),
columnname VARCHAR(50),
id INT,
counts INT
)
INSERT INTO #t
SELECT table_name [Table Name],
column_name [Column Name],
NULL,
NULL
FROM information_schema.columns
WHERE data_type = 'INT'
BEGIN TRAN
DECLARE #id INT
SET #id = 0
UPDATE #t
SET #id = id = #id + 1
COMMIT TRAN
DECLARE #RowCount INT
SET #RowCount = (SELECT Count(0)
FROM #t)
DECLARE #I INT
SET #I = 1
DECLARE #Counter INT
DECLARE #TName VARCHAR(50)
DECLARE #CName VARCHAR(50)
DECLARE #DynamicSQL AS VARCHAR(500)
WHILE ( #I <= #RowCount )
BEGIN
SELECT #TName = tablename
FROM #t
WHERE id = #I
SELECT #CName = columnname
FROM #t
WHERE id = #I
SET #DynamicSQL = 'Update #T Set Counts = '
+ '(Select ISNull(Max(' + #CName + '), 0) From '
+ #TName + ') Where Id = '
+ CONVERT(VARCHAR(10), #I)
--PRINT #DynamicSQL
EXEC (#DynamicSQL)
SET #I = #I + 1
END
SELECT *
FROM #t
END
go
Getmaxtablecolval
You can create a procedure out of this:
CREATE PROCEDURE GET_COLUMNS_WITH_MAX_VALUE
#COLUMN_TYPE NVARCHAR(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- DUMMY VARIABLE TO COPY STRUCTURE TO TEMP
DECLARE #DUMMY TABLE
(
TABLE_NAME NVARCHAR(50),
COLUMN_NAME NVARCHAR(50),
MAX_VALUE NVARCHAR(MAX)
)
-- CREATE TEMP TABLE FOR DYNAMIC SQL
SELECT TOP 0 * INTO #TABLE FROM #DUMMY
INSERT INTO #TABLE
(TABLE_NAME, COLUMN_NAME)
SELECT TABLE_NAME, COLUMN_NAME
FROM information_schema.columns where data_type = #COLUMN_TYPE
DECLARE #TABLE_NAME VARCHAR(50) -- database name
DECLARE #COLUMN_NAME VARCHAR(256) -- path for backup files
DECLARE db_cursor CURSOR FOR
SELECT TABLE_NAME, COLUMN_NAME
FROM #TABLE
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #TABLE_NAME, #COLUMN_NAME
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(MAX) = 'UPDATE #TABLE SET MAX_VALUE = (SELECT MAX([' + #COLUMN_NAME + ']) FROM [' + #TABLE_NAME + ']) '
+ 'WHERE [COLUMN_NAME] = ''' + #COLUMN_NAME + ''' AND TABLE_NAME = ''' + #TABLE_NAME + '''';
PRINT #SQL
EXEC (#SQL)
FETCH NEXT FROM db_cursor INTO #TABLE_NAME, #COLUMN_NAME
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT * FROM #TABLE
DROP TABLE #TABLE
END
GO
Usage:
EXEC GET_COLUMNS_WITH_MAX_VALUE 'INT'
Results:
TABLE1 ID 50
TABLE2 ID 100
TABLE3 CarID 20
TABLE4 StudentID 30

How do you use a T-SQL variable within an if OBJECT_ID to check if a table exists?

What I am trying to do with the following code is have it grab all the database names then loop through those databases check to see if the table tblAdminLogin exists and if it does update the password for username 'foo'
I have been using a select statement instead of an update as of yet until it works properly.
declare #databases table
(
PK int IDENTITY(1,1),
dbid int,
name varchar(50)
)
insert into #databases(dbid, name) select dbid, name from master.dbo.sysdatabases
Declare #maxPK int; select #maxPK = MAX(PK) from #databases
Declare #pk int; Set #pk = 1
Declare #name varchar(50)
While #pk <= #maxPK
Begin
Select #name = name from #databases where PK=#pk
if OBJECT_ID(''+#name+'.dbo.tblAdminLogin') IS NOT NULL
Begin
Select password from #name.dbo.tblAdminLogin where username = 'foo'
--Update #name.dbo.tblAdminLogin Set password='bar' where username = 'foo'
End
Set #pk = #pk + 1
End
The main code in question is
Select #name = name from #databases where PK=#pk
if OBJECT_ID(''+#name+'.dbo.tblAdminLogin') IS NOT NULL
Begin
Select password from #name.dbo.tblAdminLogin where username = 'foo'
--Update #name.dbo.tblAdminLogin Set password='bar' where username = 'foo'
End
Edit: Added T-SQL to title since I am using SQL Server
Edit: Fixed the Typo .dbl. to the correct .dbo.
Dynamic SQL.
Untested
declare #SQL varchar(max) -- varchar(8000) if on SQL Server 2000 or earlier
While #pk <= #maxPK
Begin
Select #name = name from #databases where PK=#pk
if OBJECT_ID(''+#name+'.dbo.tblAdminLogin') IS NOT NULL
Begin
set #SQL = 'update ' + quotename(#name) + '.dbo.tblAdminLogin Set password=''bar'' where username = ''foo'''
exec (#SQL)
End
set #PK = #PK + 1
End
References:
QUOTENAME
EXECUTE
Edit: general note on types, not asked for by OP:
The database name is of type SYSNAME which, last I checked, is equivelent to a NVARCHAR(128). Storing that value in a VARCHAR type has the chance of losing information. Small chance at some site because of the names they would choose, but a chance none the less.
declare #databases table
(
PK int IDENTITY(1,1),
dbid int,
name sysname
)
insert into #databases(dbid, name) select dbid, name from master.dbo.sysdatabases
Declare #maxPK int; select #maxPK = MAX(PK) from #databases
Declare #pk int; Set #pk = 1
Declare #name sysname -- so that
declare #SQL nvarchar(4000)
While #pk <= #maxPK
Begin
Select #name = name from #databases where PK=#pk
if OBJECT_ID(#name+N'.dbo.tblAdminLogin') IS NOT NULL
Begin
set #SQL = N'update ' + quotename(#name) + N'.dbo.tblAdminLogin Set password=''bar'' where username = ''foo'''
exec (#SQL)
End
set #PK = #PK + 1
End
Alternative:
EXEC sp_MSForEachDB
'Use [?]; if object_id(''tblAdminLogin'') is not null Select password from tblAdminLogin where username = ''foo'''
I might do something like this:
exec sp_msforeachdb '
if (object_id(''[?].[dbo].[tblAdminLogin]'', ''U'')) is not null
begin
update table [?].[dbo].[tblAdminLogin] Set password=''bar'' where username = ''foo''
select ''?'', username, password from [?].[dbo].[tblAdminLogin] where username = ''foo''
end
'

how Do I list all table names in SQL Server using T-SQL?

SELECT name FROM sys.databases -- this can list all database name in the server
user database
SELECT * FROM INFORMATION_SCHEMA.TABLES
-- these two line can list the table for one particular database
But how can I output the results like below?
Database Table
--------- -------------
db1 t1
db1 t2
db2 t1
... ...
sp_msforeachdb 'select "?" AS db, * from [?].sys.tables'
Here is a stored procedure I use constantly to list all my tables ordered by the space used by them in the database.
GO
/****** Object: StoredProcedure [dbo].[dba_SpaceUsed] Script Date: 03/16/2010 15:09:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[dba_SpaceUsed]
#SourceDB varchar ( 128 ) = null -- Optional database name
-- If omitted, the current database is reported.
, #SortBy char(1) = 'S' -- N for name, S for Size
-- T for table name
AS
SET NOCOUNT ON
DECLARE #sql nvarchar (4000)
IF #SourceDB IS NULL BEGIN
SET #SourceDB = DB_NAME () -- The current DB
END
--------------------------------------------------------
-- Create and fill a list of the tables in the database.
CREATE TABLE #Tables ( [schema] sysname
, TabName sysname )
SELECT #sql = 'insert #tables ([schema], [TabName])
select TABLE_SCHEMA, TABLE_NAME
from ['+ #SourceDB +'].INFORMATION_SCHEMA.TABLES
where TABLE_TYPE = ''BASE TABLE'''
EXEC (#sql)
---------------------------------------------------------------
-- #TabSpaceTxt Holds the results of sp_spaceused.
-- It Doesn't have Schema Info!
CREATE TABLE #TabSpaceTxt (
TabName sysname
, [Rows] varchar (11)
, Reserved varchar (18)
, Data varchar (18)
, Index_Size varchar ( 18 )
, Unused varchar ( 18 )
)
---------------------------------------------------------------
-- The result table, with numeric results and Schema name.
CREATE TABLE #TabSpace ( [Schema] sysname
, TabName sysname
, [Rows] bigint
, ReservedMB numeric(18,3)
, DataMB numeric(18,3)
, Index_SizeMB numeric(18,3)
, UnusedMB numeric(18,3)
)
DECLARE #Tab sysname -- table name
, #Sch sysname -- owner,schema
DECLARE TableCursor CURSOR FOR
SELECT [SCHEMA], TabNAME
FROM #tables
OPEN TableCursor;
FETCH TableCursor into #Sch, #Tab;
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #sql = 'exec [' + #SourceDB
+ ']..sp_executesql N''insert #TabSpaceTxt exec sp_spaceused '
+ '''''[' + #Sch + '].[' + #Tab + ']' + '''''''';
Delete from #TabSpaceTxt; -- Stores 1 result at a time
EXEC (#sql);
INSERT INTO #TabSpace
SELECT #Sch
, [TabName]
, convert(bigint, rows)
, convert(numeric(18,3), convert(numeric(18,3),
left(reserved, len(reserved)-3)) / 1024.0)
ReservedMB
, convert(numeric(18,3), convert(numeric(18,3),
left(data, len(data)-3)) / 1024.0) DataMB
, convert(numeric(18,3), convert(numeric(18,3),
left(index_size, len(index_size)-3)) / 1024.0)
Index_SizeMB
, convert(numeric(18,3), convert(numeric(18,3),
left(unused, len([Unused])-3)) / 1024.0)
[UnusedMB]
FROM #TabSpaceTxt;
FETCH TableCursor into #Sch, #Tab;
END;
CLOSE TableCursor;
DEALLOCATE TableCursor;
-----------------------------------------------------
-- Caller specifies sort, Default is size
IF #SortBy = 'N' -- Use Schema then Table Name
SELECT * FROM #TabSpace
ORDER BY [Schema] asc, [TabName] asc
ELSE IF #SortBy = 'T' -- Table name, then schema
SELECT * FROM #TabSpace
ORDER BY [TabName] asc, [Schema] asc
ELSE -- S, NULL, or whatever get's the default
SELECT * FROM #TabSpace
ORDER BY ReservedMB desc
;
DROP TABLE #Tables
DROP TABLE #TabSpaceTxt
DROP TABLE #TabSpace
--Thanks to Andrew Novick
if you need one result set, try this:
DECLARE #AllTables table (DatabaseName sysname, TableName sysname)
DECLARE #Current sysname
,#SQL1 varchar(500)
,#SQL2 varchar(500)
,#SQL3 varchar(500)
,#SQL varchar(1500)
SELECT TOP 1 #Current=Name FROM sys.databases Order By Name
SET #SQL1='select db.name, t.TABLE_NAME from '
SET #SQL2='.sys.databases db inner join '
SET #SQL3='.INFORMATION_SCHEMA.TABLES t on db.name = t.TABLE_CATALOG'
WHILE #Current IS NOT NULL
BEGIN
SET #SQL=#SQL1+#Current+#SQL2+#Current+#SQL3
INSERT INTO #AllTables
EXEC (#SQL)
SELECT TOP 1 #Current=Name FROM sys.databases WHERE Name>#Current Order By Name
IF ##ROWCOUNT=0 BREAK
END
SELECT * FROM #AllTables ORDER BY DatabaseName,TableName
If you're lucky enough to still be using sql2000:
CREATE TABLE #AllTables (DatabaseName sysname, TableName sysname)
DECLARE #Current sysname
,#SQL1 varchar(500)
,#SQL2 varchar(500)
,#SQL3 varchar(500)
,#SQL varchar(1500)
SELECT TOP 1 #Current=Name FROM sysdatabases Order By Name
SET #SQL1='select db.name, t.TABLE_NAME from '
SET #SQL2='sysdatabases db inner join '
SET #SQL3='.INFORMATION_SCHEMA.TABLES t on db.name = t.TABLE_CATALOG'
WHILE #Current IS NOT NULL
BEGIN
SET #SQL=#SQL1+#SQL2+#Current+#SQL3
--PRINT #SQL
SET NOCOUNT ON
INSERT INTO #AllTables
EXEC (#SQL)
SET NOCOUNT OFF
SELECT TOP 1 #Current=Name FROM sysdatabases WHERE Name>#Current Order By Name
IF ##ROWCOUNT=0 BREAK
END
SELECT * FROM #AllTables
--where TableName = 'at_errorlog'
ORDER BY DatabaseName,TableName
DROP TABLE #AllTables
select table_name from user_tables;

Dynamically create temp table, insert into temp table, and then select

Basically i want to be able to dynamically create a temp table based off of an existing table, and then insert values into the temp table, and select the inserted values.
i've got the part where i can create the temp table working just fine, it's just that inserting and selecting form it aren't working too well.
here's my current code.
declare #table table
(
OrdinalPosition int,
ColumnName nvarchar(255),
DataType nvarchar(50),
MaxChar int,
Nullable nvarchar(5)
)
declare #i int
declare #count int
declare #colname nvarchar(255), #datatype nvarchar(50), #maxchar int
declare #string nvarchar(max)
declare #tblname nvarchar(100)
set #tblname='Projects'
set #string='create table #' + #tblname + ' ('
insert into #table
(
OrdinalPosition,
ColumnName,
DataType,
MaxChar,
Nullable
)
SELECT
ORDINAL_POSITION ,
COLUMN_NAME ,
DATA_TYPE ,
CHARACTER_MAXIMUM_LENGTH ,
IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tblname
set #i=1
select #count=count(*) from #table
while (#i<=#count)
begin
select #colname=ColumnName from #table where OrdinalPosition=#i
select #datatype=DataType from #table where OrdinalPosition=#i
select #maxchar=MaxChar from #table where OrdinalPosition=#i
if (#maxchar is null)
begin
set #string = #string + #colname + ' ' + #datatype
end
else
begin
set #string = #string + #colname + ' ' + #datatype + '(' + cast(#maxchar as nvarchar(20)) + ')'
end
if (#i=#count)
begin
set #string = #string + ')'
end
else
begin
set #string = #string + ', '
end
set #i=#i+1
end
select #string
exec(#string)
set #string='
insert into #Projects (pk_prID, prWASSN_ID, prProjectStatus, prBusinessUnit, prServiceLine, prStudyTypeCode, prStudyNumber, prTimePoint, prStudyDirector,
prGroupLeader, prBookedDate, prBookedAmount, prConsumed, prBudgetedHours, prFinalReport, prFinalYear, prFinalMonth, prStartQA,
prLabWorkStarted, prLabWorkCompleted, prProjImpDate, prCompanyName, prCompanyNumber, prIsFTE, prRevisedDeadlineDate, prProjectFinalized,
prBookedYear, prBookedMonth, prCRMQuoteID, prLineItemNumber, prDraftReport, prInternalTargetDeadlineDate, prProtocolSignedDate,
prDataToRWS, prRWSWorkStarted, prFirstDraftToPL, prFirstDraftToQA, prArchivedDate, prToPLForQACommentReview,
prAnticipatedProjectArchiveDate, prToQAWithPLCommentResponse, prProjectReactivatedDate, prQAFinishDate, prSecondDraftReportToClient)
select *
from cube.Projects'
select #string
exec (#string)
set #string='select * from #Projects'
exec (#string)
this is the error that i get:
(44 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
Msg 208, Level 16, State 0, Line 2
Invalid object name '#Projects'.
Msg 208, Level 16, State 0, Line 1
Invalid object name '#Projects'.
Try to name the table with two ##, this will create a global temp table. It could be an issue with scoping, you might be creating the table with exec but it is not visible when it comes back.
When you call exec I believe it executes outside the context where your temp table was declared I believe if you appended your strings together and executed as one call to exec it would succeed. The other option is to use a global temp table with ## as the prefix instead of #.

Resources