T-SQL script to update numerous prod tables from temp tables - sql

I am trying to simplify the updating of numerous production tables from temp tables. I currently have a long script that uses the MERGE command, but as I increase the number of tables to be processed, the MERGE code is getting unwieldy. To simplify, my idea is to create a script that will get the table names into a cursor, get the field names for that table into a different cursor, then compare temp and prod values. If different, I will update the prod table.
The table 'TableManifest' has only 1 column called TableName.
So 2 questions
1 - Is this an efficient approach to the problem? I'd love to get other suggestions.
2 - This code fails with an error "Must declare the table variable #Temp_TableName" on the line that starts with 'IF(SELECT..'.
DECLARE #TableName varchar(MAX) -- TARGET Table to update
DECLARE #Temp_TableName varchar(MAX) -- SOURCE Table for compare
DECLARE #ColumnNames varchar(MAX) -- Table Column to compare
-- Create CURSOR of Tables to process
DECLARE C_TableNames CURSOR FOR SELECT TableName FROM TableManifest
OPEN C_TableNames
-- Get 1st table name
FETCH NEXT FROM C_TableNames INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
-- Create variable for TEMP table name
SELECT #Temp_TableName = CONCAT('TEMP_',#TableName)
-- Create CURSOR of Column Names in the table
DECLARE C_ColumnNames CURSOR FOR SELECT name FROM sys.columns WHERE object_id = OBJECT_ID(#TableName)
OPEN C_ColumNames
-- Get 1st column name
FETCH NEXT FROM C_ColumnNames INTO #ColumnNames
WHILE ##FETCH_STATUS = 0
BEGIN
-- If the column name is not 'ID' and the values are different, update the TARGET table
IF(SELECT #ColumnNames FROM #Temp_TableName) <> (SELECT #ColumnNames FROM #TableName) AND #ColumnNames <> 'id'
UPDATE #TableName SET #ColumnNames = (SELECT #ColumnNames from #Temp_TableName)
-- Get next column name
FETCH NEXT FROM C_ColumnNames INTO #ColumnNames
END
-- Get next table name
FETCH NEXT FROM C_TableNames INTO #TableName
END
-- Clean up
CLOSE C_ColumNames
DEALLOCATE C_ColumNames
CLOSE C_TableNames
DEALLOCATE C_TableNames

Related

In all tables which have a column with a given name, replace a character at a position based on the condition that it's not a certain letter

I have some set of tables where I need to find all column name containing string like Test. In these columns I have to check the actual value and if it does not have D in 4th character position. I need to update with X in the same 4th character position.
This query give me all the table and column list
SELECT t.name AS 'TableName', c.name AS 'ColumnName'
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%Test%'
ORDER BY TableName
Now for all the column list I need to write a query something like this which I am not able to figure out:
update table_name_from_above_Query
set one_such_column=Replace(oldvalue_withoutD_In4thPosition,oldvalue_withX_In4thPosition)
where one_such_column not like '___D%'
Finally I need to update value like these:
Column Old Value: ABCEFG
Column Update Value: ABCXFG
I'd first create a temporary table which holds all the tables found which match the column name ("Your query"). Then I'd use a cursor to go through all the tables in this temporary table one by one and run the update statement on them by executing a dynamic SQL query (building a string with the table names).
BEGIN TRAN
--Drop temp table if exists already
If(OBJECT_ID('tempdb..#tablesWithColumn') Is Not Null)
BEGIN
DROP TABLE #tablesWithColumn
END
--Create temporary table to store all the tables that have the matching col name
CREATE TABLE #tablesWithColumn
(
tableName varchar(3000)
)
-- Check all the tables in the system that have the column name MYCOLUMNNAME
INSERT INTO #tablesWithColumn
SELECT c.name AS 'ColumnName'
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name = 'MYCOLUMNNAME'
ORDER BY TableName
,ColumnName;
-- Create cursor for temp table that hold the current table in the list
DECLARE #currentTable VARCHAR(3000),
DECLARE table_cursor CURSOR
FOR
SELECT tableName FROM #tablesWithColumn;
OPEN table_cursor ;
-- Read next row
FETCH NEXT FROM table_cursor INTO #currentTable;
-- Loop values, run the update statement with a dynamic SQL query and get the next table to process:
DECLARE #dynSQL as varchar(max)
WHILE ##FETCH_STATUS = 0
BEGIN
-- Build the dynamic SQL string from the current table being iterated.
-- If the fourth chracter IS NOT a D, we use STUFF function to replace it.
SELECT #dynSQL = '
UPDATE '+#currentTable+'
SET MYCOLUMNNAME =
IIF(
SUBSTRING(MYCOLUMNNAME,4,1) <> ''D'',
STUFF(MYCOLUMNNAME, 4, 1, ''X''),
MYCOLUMNNAME
)'
EXEC (#dynSQL )
FETCH NEXT FROM table_cursor INTO
#currentTable
END;
--Close cursor
CLOSE cursor_product;
DEALLOCATE cursor_product;
--Drop temp table
If(OBJECT_ID('tempdb..#tablesWithColumn') IS NOT NULL)
BEGIN
DROP TABLE #tablesWithColumn
END
COMMIT
This will find ALL tables that have the column name specified and perform the replace on the column. If you wish to match all columns that match '%MYCOLUMNNAME%', you will have rewrite above query so that the temp table stores the particular variation of the column name, set a variable for it for the cursor to fetch and then also update the dynamic sql to refelect that (SET '+#MYCOLUMNNAME'+...) instead of SET MYCOLUMNNAME.

Function to select rows from multiple tables based on conditions from different tables

Can anyone please help with this query?
I’m using SQL server 2008 . Objective is to select rows from multiple tables based on condition and values from different tables .
I have table1, table2, tableN with columns as ID,ColumnName,ColumnValue . These are the table I need to select rows based on conditions from below table
Control table with columns Number,Function and Enable
Repository table with columns Function and tableName
I need pass Number and ID as parameters and get details of all Function values from Control table which has Enable value = 1 and by using these Function values collect tableNames from Repository table . And for each tableName returned from Repository table get all rows by using ID value.
The way I understand it you have two tables with schema like this:
table Control (Number int, Function nvarchar, Enable bit)
table Repository (Function nvarchar, TableName nvarchar)
Control and Repositories are related via Function column.
You also have a number of other tables and names of those tables are saved in Repositories tables. All those tables have ID column.
You want to get those table names based on a number and then select from all those tables by their ID column.
If that indeed is what you are trying to do, code bellow should be enough to solve your problem.
declare
-- arguments
#id int = 123,
#number int = 123456,
-- helper variables we'll use along the way
#function nvarchar(4000),
#tableName nvarchar(256),
#query nvarchar(4000)
-- create cursor to iterate over every returned row one by one
declare cursor #tables readonly fast_forward
for
select
c.Function,
r.TableName
from [Control] as c
join [Repository] as r on r.Function = c.Function
where c.Number = #number
and c.Enable = 1
-- initialise cursor
open #tables
-- get first row into variables
fetch next from #tables
into #function, #tableName
-- will be 0 as long as fetch next returns new values
while ##fetch_status = 0
begin
-- build a dynamic query
set #query = 'select * from ' + #tableName + ' where ID = ' + #id
-- execute dynamic query. you might get permission problems
-- dynamic queries are best to avoid, but I don't think there's another solution for this
exec(#query)
-- get next row
fetch next from #tables
into #function, #tableName
end
-- destroy cursor
close #tables
deallocate #tables

SQL Server - Select columns that meet certain conditions?

My COLUMNS can contain only three values or var chars - economy, basic, luxury. I want to select a ROW and display only those COLUMNS which contain luxury. The problem is that there are many such columns - about 50. I don't want to type the names of all those columns in my select query. Is there a shorter and simpler alternative to this ? Which query should I use ?
I am thinking of something like this (this is a FAKE query) -
#declare Column_Name varchar(30)
select Column_Name where Column_Value = 'luxury'
from ATable
where rowId = 'row 5';
Table structure -
rowId | Column1 | Column2 | Column3.....
I've created a stored procedure for you.
This procedure examines the MSSQL meta to build a dynamic SQL string that returns a result containing column names N and their values V, and the corresponding row key K from which that value was retrieved, for a specified table.
When this is executed, the results stored in a global temporary table called ##ColumnsByValue, which can then be queried directly.
Create the GetColumnsByValue stored procedure, by executing this script:
-- =============================================
-- Author: Ben Roberts (sepster#internode.on.net)
-- Create date: 22 Mar 2013
-- Description: Returns the names of columns that contain the specified value, for a given row
-- =============================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID ( 'dbo.GetColumnsByValue', 'P' ) IS NOT NULL
DROP PROCEDURE dbo.GetColumnsByValue;
GO
CREATE PROCEDURE dbo.GetColumnsByValue
-- Add the parameters for the stored procedure here
#idColumn sysname,
#valueToFind nvarchar(255),
#dbName sysname,
#tableName sysname,
#schemaName sysname,
#debugMode int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #SQL nvarchar(max);
DECLARE #SQLUnion nvarchar(max);
DECLARE #colName sysname;
DECLARE #dbContext nvarchar(256);
DECLARE #Union nvarchar(10);
SELECT #dbContext = #dbName + '.' + #schemaName + '.sp_executeSQL';
SELECT #SQLUnion = '';
SELECT #Union = '';
IF OBJECT_ID ( 'tempdb..##GetColumnsByValueIgnoreList') IS NULL -- no columns to ingore have been specified, need to create an empty list.
BEGIN
CREATE TABLE ##GetColumnsByValueIgnoreList (column_name nvarchar(255));
END
DECLARE DBcursor CURSOR FOR
SELECT
COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = #tableName
AND
TABLE_SCHEMA = #schemaName;
OPEN DBcursor;
FETCH DBcursor INTO #colName;
WHILE (##FETCH_STATUS = 0)
BEGIN
IF (
#colName != #idColumn
AND
#colName NOT IN (SELECT column_name FROM ##GetColumnsByValueIgnoreList)
)
BEGIN
SELECT #SQL = 'SELECT '+#idColumn+' as K, '''+#colName+''' as N, ' +#colName+ ' as V FROM ' + #dbName + '.' + #schemaName + '.' + #tableName;
--PRINT #SQL;
SELECT #SQLUnion = #SQL + #Union + #SQLUnion;
SELECT #Union = ' UNION ';
END
FETCH DBcursor INTO #colName;
END; -- while
CLOSE DBcursor; DEALLOCATE DBcursor;
IF (#debugMode != 0)
BEGIN
PRINT #SQLUnion;
PRINT #dbContext;
END
ELSE
BEGIN
-- Delete the temp table if it has already been created.
IF OBJECT_ID ('tempdb..##ColumnsByValue') IS NOT NULL
BEGIN
DROP TABLE ##ColumnsByValue
END
-- Create a new temp table
CREATE TABLE ##ColumnsByValue (
K nvarchar(255), -- Key
N nvarchar(255), -- Column Name
V nvarchar(255) -- Column Value
)
-- Populate it with the results from our dynamically generated SQL.
INSERT INTO ##ColumnsByValue EXEC #dbContext #SQLUnion;
END
END
GO
The SP takes several inputs as parameters, these are explained in the following code.
Note also I've provided a mechanism to add an "ignore list" as an input:
This allows you to list any column names that should not be included
in the results.
You do NOT need to add the columnn that you're using as your key, ie the row_id from your example structure.
You MUST include other columns that are not varchar as
these will cause an error (as the SP just does a varchar comparison
on all columns it looks at).
This is done via a temp table that you must create/populate
Your example table structure suggests
the table contains only columns of interest, so this may not apply to
you.
I've included example code for how to do this (but only do this if you need to):
IF OBJECT_ID ( 'tempdb..##GetColumnsByValueIgnoreList') IS NOT NULL
BEGIN
DROP TABLE ##GetColumnsByValueIgnoreList;
END
CREATE TABLE ##GetColumnsByValueIgnoreList (column_name nvarchar(255));
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('a_column');
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('another_column');
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('yet_another_column');
Now, to fire off the procedure that build your temp table of results, use the following code (and modify as appropriate, of course).
-- Build the ##ColumnsByValue table
EXEC dbo.GetColumnsByValue
#idColumn = 'row_id', -- The name of the column that contains your row ID (eg probably your PK column)
#dbName = 'your_db_name',
#tableName = 'your_table_name',
#schemaName = 'dbo',
#debugMode = 0 -- Set this to 1 if you just want a print out of the SQL used to build the temp table, to 0 if you want the temp table populated
This leaves you with ##ColumnsByValue, on which you can perform whatever search you need, eg:
select * from ##ColumnsByValue WHERE v = 'luxury' and k = 5 --some_row_id
You'd need to re-execute the stored procedure (and if relevant, create/modify the ignore list table prior to it) for each table you want to examine.
A concern with this approach is the nvarchar length might get exceeded in your case. You'd prob. need to use different datatype, reduce the column name lengths etc. Or break it up into sub-steps and union the results together to get the resultset you're after.
Another concern I have is that this is complete overkill for your particular scenario, where a one-off script-to-query-window will give you the basis of what you need, then some clever text editing in eg Notepad++ will get you all the way there... and hence this problem will likely (and quite reasonably) put you off doing it this way! But it is a good general-case question, and so deserves an answer for anyone interested in future ;-)

process each row in table in stored procedure using cursor

My Scenario is bit different. what i am doing in my stored procedure is
Create Temp Table and insert rows it in using "Cursor"
Create Table #_tempRawFeed
(
Code Int Identity,
RawFeed VarChar(Max)
)
Insert Data in temp table using cursor
Set #GetATM = Cursor Local Forward_Only Static For
Select DeviceCode,ReceivedOn
From RawStatusFeed
Where C1BL=1 AND Processed=0
Order By ReceivedOn Desc
Open #GetATM
Fetch Next
From #GetATM Into #ATM_ID,#Received_On
While ##FETCH_STATUS = 0
Begin
Set #Raw_Feed=#ATM_ID+' '+Convert(VarChar,#Received_On,121)+' '+'002333'+' '+#ATM_ID+' : Bills - Cassette Type 1 - LOW '
Insert Into #_tempRawFeed(RawFeed) Values(#Raw_Feed)
Fetch Next
From #GetATM Into #ATM_ID,#Received_On
End
Now have to process each row in Temp Table using another Cursor
DECLARE #RawFeed VarChar(Max)
DECLARE Push_Data CURSOR FORWARD_ONLY LOCAL STATIC
FOR SELECT RawFeed
FROM #_tempRawFeed
OPEN Push_Data
FETCH NEXT FROM Push_Data INTO #RawFeed
WHILE ##FETCH_STATUS = 0
BEGIN
/*
What Should i write here to retrieve each row one at a time ??
One Row should get stored in Variable..in next iteration previous value should get deleted.
*/
FETCH NEXT FROM Push_Data INTO #RawFeed
END
CLOSE Push_Data
DEALLOCATE Push_Data
Drop Table #_tempRawFeed
What Should i write In BEGIN to retrieve each row one at a time ??
One Row should get stored in Variable..in next iteration previous value should get deleted.
Regarding your last question, if what you are really intending to do within your last cursor is to concatenate RawFeed column values into one variable, you don't need cursors at all. You can use the following (adapted from your SQL Fiddle code):
CREATE TABLE #_tempRawFeed
(
Code Int IDENTITY
RawFeed VarChar(MAX)
)
INSERT INTO #_tempRawFeed(RawFeed) VALUES('SAGAR')
INSERT INTO #_tempRawFeed(RawFeed) VALUES('Nikhil')
INSERT INTO #_tempRawFeed(RawFeed) VALUES('Deepali')
DECLARE #RawFeed VarChar(MAX)
SELECT #RawFeed = COALESCE(#RawFeed + ', ', '') + ISNULL(RawFeed, '')
FROM #_tempRawFeed
SELECT #RawFeed
DROP TABLE #_tempRawFeed
More on concatenating different row values into a single string here: Concatenate many rows into a single text string?
I am pretty sure that you can avoid using the first cursor as well. Please, avoid using cursors, since the really hurt performance. The same result can be achieved using set based operations.

removing duplicates from table without using temporary table

I've a table(TableA) with contents like this:
Col1
-----
A
B
B
B
C
C
D
i want to remove just the duplicate values without using temporary table in Microsoft SQL Server. can anyone help me?
the final table should look like this:
Col1
-----
A
B
C
D
thanks :)
WITH TableWithKey AS (
SELECT ROW_NUMBER() OVER (ORDER BY Col1) As id, Col1 As val
FROM TableA
)
DELETE FROM TableWithKey WHERE id NOT IN
(
SELECT MIN(id) FROM TableWithKey
GROUP BY val
)
Can you use the row_number() function (http://msdn.microsoft.com/en-us/library/ms186734.aspx) to partition by the columns you're looking for dupes on, and delete where row number isn't 1?
I completely agree that having a unique identifier will save you a lot of time.
But if you can't use one (or if this is purely hypothetical), here's an alternative: Determine the number of rows to delete (the count of each distinct value -1), then loop through and delete top X for each distinct value.
Note that I'm not responsible for the number of kittens that are killed every time you use dynamic SQL.
declare #name varchar(50)
declare #sql varchar(max)
declare #numberToDelete varchar(10)
declare List cursor for
select name, COUNT(name)-1 from #names group by name
OPEN List
FETCH NEXT FROM List
INTO #name,#numberToDelete
WHILE ##FETCH_STATUS = 0
BEGIN
IF #numberToDelete > 0
BEGIN
set #sql = 'delete top(' + #numberToDelete + ') from #names where name=''' + #name + ''''
print #sql
exec(#sql)
END
FETCH NEXT FROM List INTO #name,#numberToDelete
END
CLOSE List
DEALLOCATE List
Another alternative would to be create a view with a generated identity. In this way you could map the values to a unique identifer (allowing for conventional delete) without making a permanent addition to your table.
Select grouped data to temp table, then truncate original, after that move back it to original.
Second solution, I am not sure will it work but you can try open table directly from SQL Management Studio and use CTRL + DEL on selected rows to delete them. That is going to be extremely slowly because you need to delete every single row by hands.
You can remove duplicate rows using a cursor and DELETE .. WHERE CURRENT OF.
CREATE TABLE Client ([name] varchar(100))
INSERT Client VALUES('Bob')
INSERT Client VALUES('Alice')
INSERT Client VALUES('Bob')
GO
DECLARE #history TABLE (name varchar(100) not null)
DECLARE #cursor CURSOR, #name varchar(100)
SET #cursor = CURSOR FOR SELECT name FROM Client
OPEN #cursor
FETCH NEXT FROM #cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
IF #name IN (SELECT name FROM #history)
DELETE Client WHERE CURRENT OF #cursor
ELSE
INSERT #history VALUES (#name)
FETCH NEXT FROM #cursor INTO #name
END