I have a linked server connecting to a Lotus notes database as the source. The destination will be an MS SQL database.
I have two temp tables. The first temp table is pulling in the table names from a linked server. From there, I want to do a record count for each table and store that value into the second temp table beside the table name.
I am having trouble trying to run a loop or cursor for each table name and then updating the second temp table with the record count for each table name.
Right now I am getting an error "Incorrect syntax near 'Execute'". SET record_count = Execute(#sqlCommand)
Declare #DB_tables table (
table_cat varchar(1500),
table_schem varchar(1500),
table_name varchar(1500),
table_type varchar(1500),
remarks varchar(1500)
)
Declare #temp_table table (
table_name varchar(1500),
record_count varchar(255),
drop_script varchar(1500),
update_script varchar(1500)
)
--Load Initial data from linked server database
insert into #DB_Tables
exec sp_tables_ex [LINKED_SERVER_DB]
--Load table name from Stored Procedure
INSERT INTO #temp_table (table_name)
SELECT table_name from #DB_Tables
--select * from #temp_table
--Variable to hold each table name in a loop or cursor
declare #tbl_name varchar(1500)
--declare #sqlCommand varchar(1500)
declare cur cursor for select table_name from #DB_Tables
Open cur
--Loop through each table name from the first temp table
--then update the second temp table (#temp_table) with the record count
FETCH NEXT FROM cur into #tbl_name
While ##FETCH_STATUS = 0 BEGIN
declare #sqlCommand varchar(1500)
--query used to get the record count from the frist temp table (#DB_tables)
SET #sqlCommand = 'select count(*) from '+#tbl_name
UPDATE #temp_table
SET record_count = Execute(#sqlCommand)
END
CLOSE cur
Deallocate cur
select * from #temp_table
It's not easy to use table variables with execute because the dynamic SQL is executed in the different context and doesn't see the variable and you can't assign results from execute that way.
You can either insert the results into a table variable with this syntax:
insert into #temp_table
execute ('select ' + #tbl_name + ', count(*) from ' + #tbl_name ...)
Or use temp. tables, since then you can refer them also inside the dynamic SQL, so you could do something like:
create table #temp_table (
table_name varchar(1500),
record_count varchar(255),
drop_script varchar(1500),
update_script varchar(1500)
)
...
Execute('update #temp_table set record_count = (select count(*) from '
+#tbl_name+') where table_name = '''+#tbl_name+''')
Related
I am attempting to develop a script to compare two databases to determine extra tables in one, then delete those tables. Here's my current script to locate the extraneous tables:
-- Any location where TARGET appears, replace TARGET with the database to be
-- modified
-- Any location where MODEL appears, replace MODEL with the database being
-- used as a model for comparison
select 'TARGET' as dbname, t1.table_name
from TARGET.[INFORMATION_SCHEMA].[tables] as t1
where table_name not in (select t2.table_name
from
MODEL.[INFORMATION_SCHEMA].[tables] as t2
)
That gives me the results I need, but now I need to fire out how to drop the tables. I'm afraid I'm utterly lost at this point. Wouldn't mind a way to declare variables instead of typing in the DBname repeatedly either, but not sure I can in this instance.
You could use dynamic SQL:
DECLARE #sql NVARCHAR(MAX) = N'';
select #sql += CONCAT('DROP TABLE ',QUOTENAME(t1.table_name,''''),';',CHAR(13))
from TARGET.[INFORMATION_SCHEMA].[tables] as t1
where table_name not in (select t2.table_name
from MODEL.[INFORMATION_SCHEMA].[tables] as t2);
SELECT #sql; -- debug
--EXEC(#sql);
EDIT:
MySQL(may need some nitpicking):
SET #s = (select GROUP_CONCAT('DROP TABLE ''' + t1.table_name + ''';' SEPARATOR CHAR(13))
from TARGET.INFORMATION_SCHEMA.tables as t1
where table_name not in (select t2.table_name
from MODEL.INFORMATION_SCHEMA.tables as t2));
SELECT #s; -- debug
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
My immediate thought is to assign each result in your query a Row Number and place your results into a temp table. Use a while loop starting at 1 and loop through the maximum number you have within that temp table getting the name of the table that Row Number is assigned to each loop. Use that name to delete from the database.
select 'TARGET' as dbname, t1.table_name
, ROW_NUMBER() OVER (Partition By t1.table_name) AS RowNumber
INTO #temp
from TARGET.[INFORMATION_SCHEMA].[tables] as t1
where table_name not in (select t2.table_name
from MODEL.[INFORMATION_SCHEMA].[tables] as t2)
DECLARE #counter INT = 1
DECLARE #maxNum INT
SELECT #maxNum = MAX(RowNumber) FROM #temp
While #counter <= #maxNum
BEGIN
DECLARE #tableName AS VARCHAR(MAX)
SELECT #tableName = table_name FROM #temp WHERE RowNumber = #counter
DELETE TABLE #tableName ' This may not be possible, but follow my lead
#counter += 1
END
DROP TABLE #temp
I am not sure if "DELETE TABLE #tableName" is a proper command but there is probably a very similar solution using what I have given you. I assume this is T-SQL..
Is it possible to do a SQL query where I can take let's say all columns form the row for user 'A' and copy them into row for user 'B' without defining the column names.
For an Example, this will not be against speed or intense data, but to help these tasks be functional instead of doing them redundantly over and over. The params for base user and target user would be the stored procedure params.
Declare #BaseUserId varchar(50),
#TargetUserId varchar(50),
#ColumnName varchar(100)
Declare columnNameCursor Cursor For SELECT c.name
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('dbo.EmployeeMenuAccess')
--Opens the cursor now for loop reading
Open columnNameCursor
Fetch next From columnNameCursor into #ColumnName
While ##Fetch_Status=0 Begin
update DocSS set #ColumnName = (select #ColumnName from DocSS where
Username = #BaseUserId) where Username = #TargetUserID
Fetch next From c into #ColumnName
End
Close columnNameCursor
Deallocate columnNameCursor
Thanks in advance.
CREATE TABLE Test
(id int, name char(10))
;
INSERT INTO Test VALUES(1, 'first');
INSERT INTO Test VALUES(2, 'second');
BEGIN TRAN T1;
SELECT * INTO Temp FROM Test WHERE id = 1;
UPDATE Temp SET id = (SELECT MAX(id)+1 FROM Test);
INSERT INTO Test SELECT * FROM Temp;
DROP TABLE Temp;
COMMIT TRAN T1;
SELECT * FROM Test;
Try this
I want to insert the values of stored procedure into a temp table without predefining the columns for the temp table.
Insert Into #Temp1 Exec dbo.sp_GetAllData #Name = 'Jason'.
How can I do this ? I saw an option as below but can I do it without mentioning the server name ?
SELECT * INTO #TestTableT FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;',
'EXEC tempdb.dbo.GetDBNames')
-- Select Table
SELECT *
FROM #TestTableT;
I could not find a possible solution without defining temp table schema and writing server name. So, I changed the code and the queries to handle with only known schema. Code example is as below
CREATE TABLE #TestTable ([name] NVARCHAR(256), [database_ID] INT);
INSERT INTO #TestTable
EXEC GetDBNames
SELECT * FROM #TestTable;
As provided in the link https://blog.sqlauthority.com/2013/05/27/sql-server-how-to-insert-data-from-stored-procedure-to-table-2-different-methods/
No-one said it had to be pretty:
CREATE PROCEDURE p AS
SELECT 1 as x, 2 as y, 3 as z
GO
DECLARE c CURSOR FOR
SELECT
name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object(OBJECT_ID('p'), 0);
DECLARE #name sysname, #type sysname;
CREATE TABLE #t(fake int)
OPEN c
FETCH NEXT from c into #name, #type
WHILE (##FETCH_STATUS = 0)
BEGIN
EXEC ('ALTER TABLE #t ADD ' + #name + ' ' + #type)
FETCH NEXT from c into #name, #type
END
CLOSE C
DEALLOCATE c
ALTER TABLE #t DROP COLUMN fake;
INSERT INTO #t EXEC p;
GO
I have two tables, Values and SpecialValues.
Values has two columns, RecordID and ValueName.
SpecialValues is a table which contains a single row, and thirty columns named SpecialValueName1, SpecialValueName2, SpecialValueName3, etc.
There are obvious database design problems with this system.
That aside, can someone explain to me how to query SpecialValues so that I can get a collection of all the values of every row from the table, and exclude them from a Select from Values?
There's probably some easy way to do this or create a View for it or something, but I think looking at this code might have broken me for the moment...
EDIT: I'd like a query to get all the individual values from every row and column of a given table (in this case the SpecialValues table) so that the query does not need to be updated the next time someone adds another column to the SpecialValues table.
This creates a #SpecialValuesColumns temporary table to store all the column names from SpecialValues.
It then uses a cursor to insert all the values from each of those columns into another temporary table #ProtectedValues.
It then uses a NOT IN query to exclude all of those values from a query to Values.
This code is bad and I feel bad for writing it, but it seems like the least-worst option open to me right now.
DECLARE #SpecialColumnsCount INT;
DECLARE #Counter INT;
DECLARE #CurrentColumnName VARCHAR(255);
DECLARE #ExecSQL VARCHAR(1024);
SET #Counter = 1;
CREATE TABLE #ProtectedValues(RecordID INT IDENTITY(1,1) PRIMARY KEY NOT NULL, Value VARCHAR(255));
DECLARE #SpecialValuesColumns TABLE (RecordID INT IDENTITY(1,1) PRIMARY KEY NOT NULL, ColumnName VARCHAR(255));
INSERT INTO #SpecialValuesColumns (ColumnName)
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'SpecialValues' AND
DATA_TYPE = 'varchar' AND
CHARACTER_MAXIMUM_LENGTH = 255
SELECT #SpecialColumnsCount = COUNT(*) FROM #SpecialValuesColumns
WHILE #Counter <= #SpecialColumnsCount
BEGIN
SELECT #CurrentColumnName = ColumnName FROM #SpecialValuesColumns WHERE RecordID = #Counter;
SET #ExecSQL = 'INSERT INTO #ProtectedValues (Value) SELECT ' + #CurrentColumnName + ' FROM SpecialValues'
EXEC (#ExecSQL)
SET #Counter = #Counter + 1;
END
SELECT * FROM Values WHERE ValueName NOT IN (SELECT ValueName COLLATE DATABASE_DEFAULT FROM #ProtectedValues)
DROP TABLE #ProtectedValues;
I might have misunderstood but doesn't this do it?
SELECT * FROM Values
WHERE ValueName NOT IN (
SELECT SpecialValueName1 FROM SpecialValues
UNION SELECT SpecialValueName2 FROM SpecialValues
UNION SELECT SpecialValueName3 FROM SpecialValues
etc..
)
You could of course make the subquery into a view instead.
*Edit:
This is quite ugly but should solve your problem:
First Create procedure #1
CREATE PROCEDURE [dbo].[SP1]
As
DECLARE
#Query nvarchar(MAX),
#Table nvarchar(255),
#Columns nvarchar(255)
CREATE TABLE #TempTable (Value nvarchar(255))
SET #Table = 'SpecialValues'
SELECT [COLUMN_NAME]
FROM [INFORMATION_SCHEMA].[COLUMNS]
WHERE [TABLE_NAME] = #Table
DECLARE Table_Cursor CURSOR FOR
SELECT COLUMN_NAME
FROM [INFORMATION_SCHEMA].[COLUMNS]
WHERE [TABLE_NAME] = #Table
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #Columns
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #TempTable EXEC SP2 #Columns = #Columns, #Table = #Table
FETCH NEXT FROM Table_Cursor INTO #Columns
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
SELECT ValueName FROM Value WHERE Value NOT IN (SELECT * FROM #TempTable)
TRUNCATE TABLE #TempTable
DROP TABLE #TempTable
Then Create procedure #2
CREATE PROCEDURE [dbo].[SP2]
#Columns nvarchar(255) = '',
#Table nvarchar(255)
AS
DECLARE
#Query nvarchar(MAX)
SET #Query = 'SELECT TOP 1 CONVERT(nvarchar, ' + #Columns + ') FROM ' + #Table
EXEC (#Query)
Then lastly execute the procedure
EXEC SP1
You need to unpivot the values in specialvalues. A pretty easy way to do that is with cross apply syntax:
select sv.value
from specialvalues sv cross apply
(values(sv.SpecialValueName1), (sv.SpecialValueName2), . . .
) sv(value)
where sv.value is not null;
You can exclude these from the list using not in, not exists or a left join.
What ever way you cut it, you have to specify the columns in SpecialValues, you can do this with a long set of UNION queries, or use UNPIVOT:
select SpecialValue
from (select SpecialValueName1,SpecialValueName2,SpecialValueName3 from #SpecialValues) p
unpivot (SpecialValue FOR ROW IN (SpecialValueName1,SpecialValueName2,SpecialValueName3))
AS unpvt
You can then incorporate this into a query on Values using NOT IN
select * from [Values] where ValueName not in (
select SpecialValue
from (select SpecialValueName1,SpecialValueName2,SpecialValueName3 from #SpecialValues) p
unpivot (SpecialValue FOR ROW IN (SpecialValueName1,SpecialValueName2,SpecialValueName3))
AS unpvt
)
I'm currently shifting roles at my job and trying to teach myself some SQL Skills.
Scenario: I'm in charge of 1 database - 10 tables with 10 Primary Keys. Every month, our code team publishes updates to the tables. I am suppose to drop the tables and generate scripts to create the updated tables.
Rather than just drop the old tables and stored procedures, I want to rename my current tables to preserve the structure/data for whatever reason.
In my database, I have an additional table called "TableUpdateList" with 1 column "TableName" and 10 rows - each row containing the name of the updated column (Row 1 = TableName1, Row 2 = TableName2, Row 3 = TableName3)
I would like to be able to "loop" through the TableUpdateList Table and insert each value into a set of SQL statements.
For Example, here are the SQL statements I want to run:
--drop the previous backup table
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME = '*TableName1*'+'_Old') DROP TABLE TableName1_Old
-- rename the current tables to _old
EXEC sp_rename *TableName1*, TableName1_Old;
I'm trying to find a way to scroll through the column of my TableUpdateList and run the above two statements filling in where I've italicized with whatever value is present in that row.
Just taking a wild stab because I think in order to get an answer here, you have to try something so here is my pseudo-code:
Declare #TableNames as List
For i in #TableNames
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME = '*i*'+'_Old') DROP TABLE TableName1_Old
-- rename the current tables to _old
EXEC sp_rename *i*, TableName1_Old;
Oi, thanks in advance for any help or a point in the right direction to where I could do some further reading about the above online.
You can use sp_executesql with CURSORS for such type of work. Here is what i think you need:
Test objects:
CREATE TABLE TableName1 ( ID INT )
GO
CREATE TABLE TableName2 ( ID INT )
GO
CREATE TABLE TableNames ( Name NVARCHAR(MAX) )
GO
INSERT INTO TableNames
VALUES ( 'TableName1' ),
( 'TableName2' )
Script itself:
DECLARE #name NVARCHAR(MAX) ,
#dropStatement NVARCHAR(MAX),
#renameStatement NVARCHAR(MAX)
DECLARE cur CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT Name
FROM dbo.TableNames
OPEN cur
FETCH NEXT FROM cur INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS ( SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #name + '_Old' )
BEGIN
SET #dropStatement = 'DROP TABLE ' + #name + '_Old'
EXEC sp_executesql #dropStatement
END
SET #renameStatement = 'sp_rename ' + #name + ', ' + #name + '_Old';
EXEC sp_executesql #renameStatement
FETCH NEXT FROM cur INTO #name
END
CLOSE cur
DEALLOCATE cur
After this you should add TableName1 and TableName2 again.
Cursors must be avoided as long as possible.
--Preparing script which would check if the old tables exists. If it does,
--it drops the old table
--e.g. first the value 'Table1' is found in TableUpdateList table.
--Then, Table1_Old is deleted and Table1 is renamed to Table1_Old
SELECT 'DROP TABLE ' + b.name + '_Old; EXEC sp_rename ''' + b.name+ ''', ''' + b.name+ '_Old;''' AS [Action]
INTO #Action
FROM INFORMATION_SCHEMA.TABLES A JOIN TableUpdateList B ON A.TABLE_NAME = b.NAME + '_Old'
DECLARE #sql VARCHAR(8000)
SELECT #sql = COALESCE(#sql + ' ', '') + [Action]
FROM #Action
select #sql
--EXEC (#sql)
First verify the value of variable #sql. Then, uncomment the last line to execute the code.
SQL fiddle