sql copy specific columns from one table to another table - sql-server

In the sql server I have a Codes table in DevDB. It has 10 columns. There is a foreign key column as ParentCodeID, int column.
I need to copy 5 columns excluding the int column to the TestDB. But I need to include "33" as the ParentcodeID to this column.
There are like 250+ records. I need to do this automatically.
Please help!!!!

Try this,
INSERT INTO TestDB.dbo.Codes(Col1,Col2,Col3,....)
SELECT 33,col1,col2.... FROM DevDB.dbo.tblCode where ParentCodeID = xx

You can automate even the generation of the script to avoid corrections in future if new columns would be added. Here is the code:
declare #tblName varchar(100),#FieldList varchar(max),#FieldListReplaced varchar(max),#sql varchar(max),#Field varchar(100),#ReplacedField varchar(100),#ReplaceWithValue varchar(100)
set #tblName='Codes'
set #ReplacedField='ParentcodeID'
set #ReplaceWithValue='33'
set #FieldList=','
declare cur_fieldlist cursor for
select [Name] from sys.all_columns
where object_id=(Select id from sysobjects where name=#tblName) and
is_computed=0 and (is_identity=0)
order by column_id
open cur_fieldlist
fetch next from cur_fieldlist into #Field
while ##fetch_status = 0
begin
set #FieldList=#FieldList+#Field+','
fetch next from cur_fieldlist into #Field
end
close cur_fieldlist
deallocate cur_fieldlist
set #FieldListReplaced=replace(#FieldList,','+#ReplacedField+',',','+#ReplaceWithValue+',') /*to avoid replacement for several fields like ParentcodeID, ParentcodeID2, BigParentcodeID, BigParentcodeID2 etc. */
set #FieldList=substring(#FieldList,2,(len(#FieldList)-2))
set #FieldListReplaced=substring(#FieldListReplaced,2,(len(#FieldListReplaced)-2))
set #sql='INSERT INTO TestDB.dbo.'+#tblName+' ('+#FieldList+')
SELECT '+#FieldListReplaced+' FROM DevDB.dbo.'+#tblName+' WHERE 1=1 /* put your conditions here */'
print (#sql) --for checking
exec (#sql)

Related

How to run series of values through stored procedure

I have a stored procedure that I need to run a list of values through and output into a temp table.
This is the SP: EXEC [SP_ReturnHTML] #zoneid, 1
The first value, I assume, will be a variable and the second value will be hard-coded. I am not able to modify this SP, as it is used in other processes, so I need to run these values through the SP via a cursor or WHILE loop. The values only need to be run through once, so a FAST_FORWARD cursor type may be more ideal, based on some preliminary reading on cursors (of which my experience in is extremely limited). This is what I attempted:
declare #zoneid int = (select zoneid from #values)
declare list cursor fast_forward
for EXEC [SP_ReturnHTML] #zoneid,1
open list
fetch next from list
But when I try to do this, I get the error Incorrect syntax near the keyword 'EXEC'.
The output of this SP, when using #zoneid=14105 (and the hard-coded 1 relates to the fieldgroupid) looks something like the shot below. For clarity, despite using #zoneid=14105, the reason a value of 4054 shows up is due to the way the SP is written, and is intended. The two values relate to a state and county relationship, noted by the first 2 columns, ParentHeaderId and HeaderId. I opted to use 14105 for the example, because the 3 examples in the #values table only retrieve their secondary value and I wanted to avoid confusion here.
The values that I need to run through the SP for the #zoneid are in a table (which has about 3100 rows), which can be exemplified with the following:
create table #values (zoneid int)
insert into #values
values
(13346),
(13347),
(13348)
So very simply put, I need something like the following as a final product (pseudo code):
declare #zoneid INT = (select zoneid from #values)
select * into #results from
(
EXEC [SP_ReturnHTML] #zoneid, 1
)
Something like this:
drop table if exists #results
drop table if exists #Data
go
create or alter procedure [SP_ReturnHTML] #value int, #s varchar(20)
as
begin
select concat(' value=',#value, '; s = ', #s)
end
go
create table #Data (value int, county varchar(30))
insert into #Data
values
(100, 'Baker'),
(101,'Baldwin'),
(102,'Baldwin'),
(103,'Ballard'),
(104,'Baltimore City'),
(105,'Baltimore'),
(106,'Bamberg'),
(107,'Bandera'),
(108,'Banders'),
(109,'Banks'),
(110,'Banner'),
(111,'Bannock'),
(112,'Baraga')
go
create table #results(value nvarchar(200))
declare c cursor local for select value from #Data
declare #value int
open c
fetch next from c into #value
while ##fetch_status = 0
begin
insert into #results(value)
EXEC [SP_ReturnHTML] #value, '1'
fetch next from c into #value
end
go
select *
from #results

I am searching for a loop query over multiple databases and insert result into existing table in one database to collect al data

I am searching for a loop query over multiple databases and insert result into existing table in one database to collect al data.
There are 28 existing databases at the moment but when i start the query below it says table already exists at the second database.
when this works i want to loop a much larger query then this.
I also tried executing and union all but if a new database is added it must be collected autmatically.
See example i've tried below:
--drop table if exists [hulptabellen].dbo.HIdatabases
declare #dbList table (dbName varchar(128), indx int)
insert into #dbList
select dbName = dbname, row_number() over (order by dbname)
from [hulptabellen].dbo.HIdatabases
--declare variables for use in the while loop
declare #index int = 1
declare #totalDBs int = (select count(*) from #dbList)
declare #currentDB varchar(128)
declare #cmd varchar(300)
--define the command which will be used on each database.
declare #cmdTemplate varchar(300) =
'
use {dbName};
select * insert into [hulptabellen].dbo.cladrloc from {dbname}.dbo.cladrloc
'
--loop through each database and execute the command
while #index <= #totalDBs
begin
set #currentDB = (select dbName from #dbList where indx = #index)
set #cmd = replace(#cmdTemplate, '{dbName}', #currentDB)
execute(#cmd)
set #index += 1
end
Create the table outside your loop and insert into the table this way:
INSERT INTO [hulptabellen].dbo.cladrloc (col1,col2)
SELECT col1,col2
FROM {dbname}.dbo.cladrloc
FYI: When you use the following syntax, a new table is created, so it can be executed only once.
SELECT *
INTO [hulptabellen].dbo.cladrloc
FROM {dbname}.dbo.cladrloc

How to optimize cursor in a stored procedure

I'm having problems with a stored procedure that iterates over a table, it works fine with a few hundred rows however when the table is over the thousands it saturates the memory and crashes.
The procedure should iterate row by row and fill a column with a value which is calculated from another column in the row. I suspect it is the cursor that crashes the procedure and in other questions I've read to use a while loop but I'm no expert in sql and the examples I tried from those answers didn't work.
CREATE PROCEDURE [dbo].[GenerateNewHashes]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #module BIGINT = 382449983
IF EXISTS(SELECT 1 FROM dbo.telephoneSource WHERE Hash IS NULL)
BEGIN
DECLARE hash_cursor CURSOR FOR
SELECT a.telephone, a.Hash
FROM dbo.telephoneSource AS a
OPEN hash_cursor
FETCH FROM hash_cursor
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE dbo.telephoneSource
SET Hash = CAST(telephone AS BIGINT) % #module
WHERE CURRENT OF hash_cursor
FETCH NEXT FROM hash_cursor
END
CLOSE hash_cursor
DEALLOCATE hash_cursor
END
END
Basically the stored procedure is intended to fill a new column called Hash that was added to the existing table, when the script that updates the table ends the new column is filled with NULL values and then this stored procedure is supposed to fill each null value with the operation telephone number (which is a bigint) % module variable (big int as well).
Is there anything besides changing to a while loop that I can do to make it use less memory or just don't crash? Thanks in advance.
You could do the following:
WHILE 1=1
BEGIN
UPDATE TOP (10000) dbo.telephoneSource
SET Hash = CAST(telephone AS BIGINT)%#module
WHERE Hash IS NULL;
IF ##ROWCOUNT = 0
BEGIN
BREAK;
END;
END;
This will update Hash as long as there are NULL values and will exit once there have been no records updated.
Adding a filtered index could be useful as well:
CREATE NONCLUSTERED INDEX IX_telephoneSource_Hash_telephone
ON dbo.telephoneSource (Hash)
INCLUDE (telephone)
WHERE Hash IS NULL;
It will speed up lookups in order to update it. But this might be not needed.
Here is example of code to do it in loops from my comment above with out using a cursor, and if you add where your field you are updating IS NOT NULL into the inner loop it wont update ones that were already done (in case you need to restart the process or something.
I didnt include your specific tables in there but if you need me to I can add it in there.
DECLARE #PerBatchCount as int
DECLARE #MAXID as bigint
DECLARE #WorkingOnID as bigint
Set #PerBatchCount = 1000
--Find range of IDs to process using yoru tablename
SELECT #WorkingOnID = MIN(ID), #MAXID = MAX(ID)
FROM YouTableHere WITH (NOLOCK)
WHILE #WorkingOnID <= #MAXID
BEGIN
-- do an update on all the ones that exist in the offer table NOW
--DO YOUR UPDATE HERE
-- include this where clause where ID is your PK you are looping through
WHERE ID BETWEEN #WorkingOnID AND (#WorkingOnID + #PerBatchCount -1)
set #WorkingOnID = #WorkingOnID + #PerBatchCount
END
SET NOCOUNT OFF;
I would simply add computed column:
ALTER TABLE dbo.telephoneSource
ADD Hash AS (CAST(telephone AS BIGINT)%382449983) PERSISTED;

Attempting to change databases using cursor loop, but database won't change

I'm writing a script that, when executed, will drop any tables in a list of given DBs if the DB Name + Table Name combo does not exist in a table named CleanUpTableList. All DBs reside on the same server. I am using SQL Server 2014.
I am trying to do this by creating an outer cursor loop which cycles through the list of DB names and an inner cursor loop which pulls in a list of table names within the given database which are not found in CleanUpTableList and drops those tables. However, it seems that the outer loop fails to change databases. The script will only access the relevant tables of the starting database X times, with X being however many database name entries there are in the outer cursor. So, for example, if I start in Database1, and I have three database name entries in my outer cursor, instead of getting:
DROP TABLE Database1..TableB
DROP TABLE Database1..TableC
DROP TABLE Database2..TableE
DROP TABLE Database2..TableF
DROP TABLE Database3..TableH
DROP TABLE Database3..TableI
I get:
DROP TABLE Database1..TableB
DROP TABLE Database1..TableC
DROP TABLE Database1..TableB
DROP TABLE Database1..TableC
DROP TABLE Database1..TableB
DROP TABLE Database1..TableC
...Which is not really what I want, so I am assuming something is amiss in the outer loop. I know the usual DB change command is
USE Database1;
GO
But I wasn't able to figure out how to do that with EXEC(). It kept telling me there was a syntax error near GO, I assume because GO can't be on the same line as 'USE Database1;', and I don't know how make a new line when using EXEC(). I tried using
SET #ChangeDB = 'USE ' + #DatabaseName + ';'
EXEC(#ChangeDB + CHAR(13) + 'GO')
and
SET #ChangeDB ='USE ' + #DatabaseName + ';' +CHAR(13) + 'GO'
EXEC(#ChangeDB)
but these also returned a syntax error.
Here is the relevant code:
DB/Table Creation Script:
CREATE DATABASE Database1;
CREATE DATABASE Database2;
CREATE DATABASE Database3;
CREATE DATABASE Database4;
CREATE TABLE Database1.dbo.TableA (Column1 INT, Column2 INT);
CREATE TABLE Database1.dbo.TableB (Column1 INT, Column2 INT);
CREATE TABLE Database1.dbo.TableC (Column1 INT, Column2 INT);
CREATE TABLE Database2.dbo.TableD (Column1 INT, Column2 INT);
CREATE TABLE Database2.dbo.TableE (Column1 INT, Column2 INT);
CREATE TABLE Database2.dbo.TableF (Column1 INT, Column2 INT);
CREATE TABLE Database3.dbo.TableG (Column1 INT, Column2 INT);
CREATE TABLE Database3.dbo.TableH (Column1 INT, Column2 INT);
CREATE TABLE Database3.dbo.TableI (Column1 INT, Column2 INT);
CREATE TABLE Database4.dbo.CleanUpTableList (DBName VARCHAR(20), TableName VARCHAR(20));
INSERT INTO Database4..CleanUpTableList VALUES ('Database1','TableA')
INSERT INTO Database4..CleanUpTableList VALUES ('Database2','TableD')
INSERT INTO Database4..CleanUpTableList VALUES ('Database3', 'TableG')
Clean Up Script:
DECLARE #fetch_database_cursor INT
DECLARE #DatabaseName VARCHAR(50)
DECLARE DatabaseList CURSOR FOR
select name from sys.databases
where
name IN ('Database1','Database2', 'Database3'
)
OPEN DatabaseList
FETCH NEXT FROM DatabaseList INTO #DatabaseName
/* Keep track of the outer loop FETCH_STATUS in a local variable */
SET #fetch_database_cursor = ##FETCH_STATUS
/* Use outer loop FETCH_STATUS local variable as condition for outer WHILE loop */
WHILE #fetch_database_cursor = 0
BEGIN
DECLARE #ChangeDB VARCHAR(2500)
DECLARE #TableName VARCHAR(50)
DECLARE #ExecuteSQL VARCHAR(2500)
DECLARE #fetch_table_cursor INT
/* Change DB here */
SET #ChangeDB = 'USE ' + #DatabaseName
EXEC(#ChangeDB)
/* Declare inner cursor */
DECLARE TableList CURSOR FOR
select table_name
from information_schema.tables
WHERE TABLE_TYPE = 'BASE TABLE'
AND table_name NOT IN (
SELECT TableName
FROM Database4..CleanUpTableList
WHERE DBName = #DatabaseName
)
ORDER BY table_name
OPEN TableList
FETCH NEXT FROM TableList INTO #TableName
/* Store inner cursor fetch_status in local variable */
SET #fetch_table_cursor = ##FETCH_STATUS
/* Use inner cursor fetch_status local variable as condition for inner WHILE loop */
WHILE #fetch_table_cursor = 0
BEGIN
SET #ExecuteSQL = 'DROP TABLE ' +#Tablename
EXEC(#ExecuteSQL)
SELECT #Tablename, 'Has Been Successfully Dropped'
FETCH NEXT FROM TableList INTO #TableName
SET #fetch_table_cursor=##FETCH_STATUS
END
/* Close and deallocate inner cursor */
CLOSE TableList
DEALLOCATE TableList
FETCH NEXT FROM DatabaseList INTO #DatabaseName
SET #fetch_database_cursor = ##FETCH_STATUS
END
/* Close and deallocate outer cursor */
CLOSE DatabaseList
DEALLOCATE DatabaseList
Any suggestions are appreciated.
From Your code ,i understood that you are trying to do same operation in all databases,that can be implemented by sp_msforeachdb..
--all databases
EXECUTE master.sys.sp_MSforeachdb
'USE [?];
if db_id()<=4 return;
drop table dbo.sometable'
--run only few databases..
EXECUTE master.sys.sp_MSforeachdb
'USE [?];
if db_name(db_id()) in (''master'',''tempdb'') --your dbnames
Begin
select db_name() --your query
end'
You could also use Aaron Bertrand's rewrite of Sp_msforeachDB which also can deal with some limitations of Sp_msforeachdb :Making a more reliable and flexible sp_MSforeachdb
Instead of trying to execute the Use database statement, try fully qualifying the database.dbo.tablename in the drop statement. You have all of the database and table names.

Creating tables based off of SchemaColumn & SchemaTable composite table

I wanted to create a new set of tables in a db using a stored procedure and a build table.
The build table includes the following columns and some sample rows:
tblNm colNm colTyp colLen colReq colWarning colUni colComUni
account personID Decimal NULL 0 0 0 0
account studentNum String 15 0 0 0 0
I was considering using multiple cursors as a form of nested looping, but I cannot figure out how to define the column parameters in the nested procedure because cursors only return one value.
I am considering to build an alter statement that parses these values. How could I do this?
You can solve the problem by using two cursors or one cursor. Two cursors will make the code more readable. One cursor will be more efficient.
Two cursors
The code below demonstrates how to use two cursors to iterate through the tables and columns.
DECLARE #tblNm VARCHAR(MAX)
DECLARE cTables CURSOR FOR
SELECT tblNm
FROM CompositeSchema
GROUP BY tblNm
ORDER BY tblNm
OPEN cTables
FETCH cTables INTO #tblNm
WHILE ##FETCH_STATUS=0
BEGIN
PRINT 'Processing table '+#tblNm
-- Start of code to execute for each table
DECLARE #sqlCreateTable VARCHAR(MAX)
SET #sqlCreateTable = 'CREATE TABLE ['+#tblNm+'] ('
DECLARE #colNm VARCHAR(MAX),#colTyp VARCHAR(MAX),#colLen INT,#colReq BIT,#colWarning BIT,#colUni BIT,#colComUni BIT
DECLARE #isFirst BIT
SET #isFirst = 1
DECLARE cCols CURSOR FOR
SELECT colNm,colTyp,colLen,colReq,colWarning,colUni,colComUni
FROM CompositeSchema
WHERE tblNm=#tblNm
ORDER BY colComUni DESC,colNm ASC
OPEN cCols
FETCH cCols INTO #colNm,#colTyp,#colLen,#colReq,#colWarning,#colUni,#colComUni
WHILE ##FETCH_STATUS=0
BEGIN
PRINT 'Processing column ['+#tblNm+'].['+#colNm+']'
-- Start of code to process each column (simplified!)
IF #isFirst=0
SET #sqlCreateTable = #sqlCreateTable+','
SET #isFirst = 0
SET #sqlCreateTable = #sqlCreateTable+'['+#colNm+'] '+#colTyp
IF NOT #colLen IS NULL
SET #sqlCreateTable = #sqlCreateTable+'('+CAST(#colLen AS VARCHAR)+')'
-- End of code to process each column
FETCH cCols INTO #colNm,#colTyp,#colLen,#colReq,#colWarning,#colUni,#colComUni
END
CLOSE cCols
DEALLOCATE cCols
SET #sqlCreateTable = #sqlCreateTable+')'
PRINT #sqlCreateTable
-- EXEC(#sqlCreateTable)
-- End of code to execute for each table
FETCH cTables INTO #tblNm
END
CLOSE cTables
DEALLOCATE cTables
One cursor
In this case we use just one cursor. We keep track of what the current table is that we are processing in the #currentTblNm variable. Whenever the variable changes, we create all columns at once.
DECLARE #currentTblNm VARCHAR(MAX),#sqlCreateTable VARCHAR(MAX)
SET #currentTblNm = ''
DECLARE #tblNm VARCHAR(MAX),#colNm VARCHAR(MAX),#colTyp VARCHAR(MAX),#colLen INT,#colReq BIT,#colWarning BIT,#colUni BIT,#colComUni BIT
DECLARE #isFirst BIT
SET #isFirst = 1
DECLARE cCols CURSOR FOR
SELECT tblNm,colNm,colTyp,colLen,colReq,colWarning,colUni,colComUni
FROM CompositeSchema
ORDER BY tblNm ASC,colComUni DESC,colNm ASC
OPEN cCols
FETCH cCols INTO #tblNm,#colNm,#colTyp,#colLen,#colReq,#colWarning,#colUni,#colComUni
WHILE ##FETCH_STATUS=0
BEGIN
IF #currentTblNm<>#tblNm
BEGIN
IF #sqlCreateTable<>''
BEGIN
SET #sqlCreateTable = #sqlCreateTable+')'
PRINT #sqlCreateTable
--EXEC (#sqlCreateTable)
END
SET #isFirst = 1
SET #sqlCreateTable = 'CREATE TABLE ['+#tblNm+'] ('
SET #currentTblNm = #tblNm
PRINT 'Processing table ['+#tblNm+']'
END
-- Start of code to process each column (simplified!)
IF #isFirst=0
SET #sqlCreateTable = #sqlCreateTable+','
SET #isFirst = 0
SET #sqlCreateTable = #sqlCreateTable+'['+#colNm+'] '+#colTyp
IF NOT #colLen IS NULL
SET #sqlCreateTable = #sqlCreateTable+'('+CAST(#colLen AS VARCHAR)+')'
-- End of code to process each column
FETCH cCols INTO #tblNm,#colNm,#colTyp,#colLen,#colReq,#colWarning,#colUni,#colComUni
END
CLOSE cCols
DEALLOCATE cCols
IF #sqlCreateTable<>''
BEGIN
SET #sqlCreateTable = #sqlCreateTable+')'
PRINT #sqlCreateTable
-- EXEC(#sqlCreateTable)
END
Both pieces of code, Two cursors and One cursor are simplified. The logic to properly create all of the constraints (like primary key, unique constraints, foreign keys etc.), the logic to properly map the column data types, and not to forget, the making the distinction between creating a new table and altering an existing table is beyond the scope of this post.
Worth mentioning is that you could also use declarative SQL code with FOR XML to create the table structure. This is possible and would be able to generate the CREATE TABLE statements with a much better performance. From experience I know that this code will be much harder to maintain and you might run into the limitations of declarative SQL.

Resources