SQL Merge statement with input table name - sql

I have a scenario to copy the data from one table to another table. While copying, I need to store the Identity values of table1 and table2.
create procedure procedure1
(#sourceClientNumber int,
#destinationClientNumber int,
#statusID varchar(10))
as
declare #scriptSql nvarchar(max);
declare #Temp_ClientScriptTable table
(newScriptID int,
oldScriptID int);
begin
SET #CreatedBy = 'Initaial Creation'
SET #CreatedByName= 'Initial Creation'
SET #CreatedDateTime = GETDATE()
SET #scriptSql = 'Merge into [dbo].[Client_'+#destinationClientNumber +'_Script] using
(select ScriptID,
ScriptName,
ScriptVersion,
Description,
FacilityID,
StatusID,
Shared,
ScriptTYpeID
from [dbo].[Client_'+#sourceClientNumber +'_Script]
where statusID = ' +#statusID + '
) scripts on 1 = 0
When not matched then
insert ([ScriptName],
[ScriptVersion],
[CreatedBy],
[CreatedByName],
[CreatedDateTime],
[Description],
[FacilityID],
[StatusID],
[Shared],
[ScriptTypeID])
values (scripts.ScriptName,
scripts.ScriptVersion,'
+ #CreatedBy + ','
+ #CreatedByName + ','
+ #CreatedDateTime + ',
scripts.Description,
scripts.FacilityID,
scripts.StatusID,
scripts.Shared,
scripts.ScriptTypeID)
output Inserted.ScriptID, scripts.ScriptID
into' + #Temp_ClientScriptTable + '(newScriptID, oldScriptID)'
EXECUTE sp_executesql #scriptSql
I am getting the error at #Temp_ClientScriptTable.
Could you please help me on this..

The main problem here is faulty database design.
The fact that you have multiple tables with the same structure, only different by a number inside the name - means you are mixing data (the number) and meta data (the table name).
Instead of having a different table for each client, you should add the client number as a column to a single table.
If you can do that, it will also eliminate the need for using dynamic SQL everywhere you need to address this table.

Root cause is, #Temp_ClientScriptTable is a table variable and cannot participate in string concatenation.
Your last line of #scriptSql should be -
into #Temp_ClientScriptTable(newScriptID, oldScriptID)'
instead of
into' + #Temp_ClientScriptTable + '(newScriptID, oldScriptID)'
Also you need to add single quotes around varchar variables while using them in string concatenation. Your final #scriptSql will be -
SET #scriptSql = 'Merge into [dbo].[Client_'+#destinationClientNumber +'_Script] using
(select ScriptID,
ScriptName,
ScriptVersion,
Description,
FacilityID,
StatusID,
Shared,
ScriptTYpeID
from [dbo].[Client_'+#sourceClientNumber +'_Script]
where statusID = ''' +#statusID + '''
) scripts on 1 = 0
When not matched then
insert ([ScriptName],
[ScriptVersion],
[CreatedBy],
[CreatedByName],
[CreatedDateTime],
[Description],
[FacilityID],
[StatusID],
[Shared],
[ScriptTypeID])
values (scripts.ScriptName,
scripts.ScriptVersion,'''
+ #CreatedBy + ''','''
+ #CreatedByName + ''','
+ #CreatedDateTime + ',
scripts.Description,
scripts.FacilityID,
scripts.StatusID,
scripts.Shared,
scripts.ScriptTypeID)
output Inserted.ScriptID, scripts.ScriptID
into #Temp_ClientScriptTable(newScriptID, oldScriptID)'
To debug these kind of issue, you should always print the the concatenated query and see how your query will look like -
print #scriptSql

Related

Dynamic SQL where condition with values from another table

I want to build a dynamic SQL query where I can use data from another table as where condition. Let's assume I have two tables: one table with financial data and the other one with conditions. They look something like this:
Table sales
c006 mesocomp c048 c020 c021
----- ---------- ------- ----- ----
120 01TA MICROSOFT 2 239
and a condition table with the following data:
dimension operator wert_db
--------- -------- -------
sales.c006 < 700
sales.c048 not like 'MIC%'
sales.c021 in (203,206)
I want to select all data from sales with the conditions stated in the condition table. So I have an SQL Query as follows:
SELECT *
FROM sales
WHERE sales.c006 < 700
AND sales.c048 NOT LIKE 'MIC%'
AND sales.c021 IN (203, 206)
Since you've posted no attempt to solve or research this yourself, I'll point you in a direction to get you started.
Your question already mentions using Dynamic SQL, so I assume you know at least what that is. You're going to populate a string variable, starting with 'SELECT * FROM Sales '.
You can use the STUFF...FOR XML PATH technique to assemble the conditions rows into a WHERE clause.
One change to the linked example is that you'll need to concatenate dimension, operator and wert_db into one artificial column in the innermost SELECT. Also instead of separating with a comma, you'll separate with ' AND '. And change the parameters of the STUFF function to take off the length of ' AND ' instead of the length of a comma.
DECLARE #tblSales TABLE
(
c006 VARCHAR(10),
mesocomp VARCHAR(100),
c048 VARCHAR(100),
c020 VARCHAR(100),
c021 VARCHAR(100)
)
INSERT INTO #tblSales(c006, mesocomp, c048, c020, c021)
VALUES(120,'01Ta','Microsoft','2','239')
SELECT * FROM #tblSales
DECLARE #tblCondition TABLE
(
Id INT,
dimension VARCHAR(100),
operator VARCHAR(10),
wert_db VARCHAR(100)
)
INSERT INTO #tblCondition(Id, dimension, operator, wert_db) VALUES(1,'sales.c006','<','700')
INSERT INTO #tblCondition(Id, dimension, operator, wert_db) VALUES(1,'sales.c048','not like','''MIC%''')
INSERT INTO #tblCondition(Id, dimension, operator, wert_db) VALUES(1,'sales.c021','in','(203,206)')
DECLARE #whereCondition VARCHAR(400)
SELECT #whereCondition = COALESCE(#whereCondition + ' ', '') + dimension + ' ' + operator + ' ' + wert_db + ' AND '
FROM #tblCondition
SET #whereCondition = SUBSTRING(#whereCondition,0, LEN(#whereCondition) - 3)
PRINT #whereCondition
DECLARE #sql VARCHAR(4000)
SET #sql = 'SELECT * FROM #tblSales Where ' + #whereCondition
PRINT #sql
EXEC(#sql)
--please use real tables so you will get everything working.

Sql Trigger Error explaination?

I have been working on this trigger for a while and seem to be stuck. I feel that my logic is somewhat correct but when I run the code, I get this error message:
Msg 213, Level 16, State 1, Procedure Message_Student_ForInsert, Line 9
Column name or number of supplied values does not match table definition.
Here is my code:
Create Trigger Message_Student_ForInsert
ON CourseEnrolled
For Insert
AS
Begin
Declare #Id int
Select #Id = StudentID from inserted
insert into CourseAudit
Values ('New Student with Id = ' + Cast(#Id as nvarchar(10)) + ' is added at ' + cast(GetDate() as nvarchar(30)))
END
I have created a table name CourseAudit as well in an attempt to try and solve this error. I thought the error meant that I am missing values inside of the tables - since I haven't inserted any yet. Any help would be appreciated, Thanks!
Your INSERT statement is incorrect. Learn more here; INSERT()
Based on your code, one assumes the table [CourseAudit] exists and has a nvarchar() column in it.
You need to specify the column name for the insert.
If you omit the column name, then you should suply a value for ALL columns, in the order on which they appear in the table definition.
Example:
insert into CourseAudit (SomeCulumnNameHere)
Values ('New Student with Id = ' + Cast(#Id as nvarchar(10))
+ ' is added at ' + cast(GetDate() as nvarchar(30)))

Insert query where one field is a dynamic list of values

I'm looking to solve the challenge of having an insert query where action_key is a dynamic list of strings. The cust_name and action fields will be the same for each new row, but action_key will change depending on how many action keys are in the list. Example:
INSERT INTO [table_name] (cust_name, action, action_key) VALUES (Bob, Approve, ApproveId1)
INSERT INTO [table_name] (cust_name, action, action_key) VALUES (Bob, Approve, ApproveId2) etc.
I'd appreciate some insight on this topic, thanks.
You'll have to play with it, but based upon your XML structure input that's how I handled the parsing.
Declare #cust_name Varchar(50),
#action Varchar(50),
#action_key Varchar(1000)
Select #cust_name = 'Bob',
#action = 'Approve',
#action_key = '<action_keys><action_key>ApproveId1</action_key><action_key>ApproveId2</action_key><action_key>ApproveId3</action_key><action_key>ApproveId4</action_key></action_keys>'
Select #cust_name,
#action,
n.r.value('.', 'varchar(50)')
From (Select Cast(#action_key As Xml)) As s(XMLCol)
Cross Apply s.XMLCol.nodes('action_keys/action_key') as n(r)
Reference: How Do I Split a Delimited String in SQL Server Without Creating a Function?
Well, you don't say how you get the list of strings, but generally you could create a SQL string dynamically and execute it:
EXEC ('INSERT INTO [table_name] ' +
'(cust_name, action, ' + action_key + ') ' +
'VALUES (''Bob'', ''Approve'', ''ApproveId1'')')

SQL Server Compare similar tables with query

Simple concept we are basically doing some auditing, comparing what came in, and what actually happened during processing. I am looking for a better way to execute a query that can do side by side table comparisons with columns that are slightly differnt in name and potentialy type.
DB Layout:
Table (* is the join condition)
Log (Un-altered data record.)
- LogID
- RecordID*
- Name
- Date
- Address
- Products
- etc.
Audit (post processing record)
- CardID*
- CarName
- DeploymentDate
- ShippingAddress
- Options
- etc.
For example this would work if you look past the annoying complexity to write, and performance issues.
The query just joins the left and right and selects them as strings. Showing each field matched up.
select
cast(log.RecordID as varchar(40)) + '=' + cast(audit.CardID as varchar(40),
log.Name+ '=' + audit.Name ,
cast(log.Date as varchar(40)) + '=' + cast(audit.DeploymentDate as varchar(40),
log.Address + '=' + audit.ShippingAddress,
log.Products+ '=' + audit.Options
--etc
from Audit audit, Log log
where audit.CardID=log.RecordId
Which would output something like:
1=1 Test=TestName 11/09/2009=11/10/2009 null=My Address null=Wheels
This works but is extremely annoying to build. Another thing I thought of was to just alias the columns, union the two tables, and order them so they would be in list form. This would allow me to see the column comparisons. This comes with the obvious overhead of the union all.
ie:
Log 1 Test 11/09/2009 null, null
Audit 1 TestName 11/10/2009 My Address Wheels
Any suggestions on a better way to audit this data?
Let me know what other questions you may have.
Additional notes. We are going to want to reduce the unimportant information so in some cases we might null the column if they are equal (but i know its too slow)
case when log.[Name]<>audit.[CarName] then (log.[Name] + '!=' + audit.[CarName]) else null end
or if we are doing the second way
nullif(log.[Name], audit.[CarName]) as [Name]
,nullif(audit.[CarName], log.[Name]) as [Name]
I've found the routine given here by Jeff Smith to be helpful for doing table comparisons in the past. This might at least give you a good base to start from. The code given on that link is:
CREATE PROCEDURE CompareTables(#table1 varchar(100),
#table2 Varchar(100), #T1ColumnList varchar(1000),
#T2ColumnList varchar(1000) = '')
AS
-- Table1, Table2 are the tables or views to compare.
-- T1ColumnList is the list of columns to compare, from table1.
-- Just list them comma-separated, like in a GROUP BY clause.
-- If T2ColumnList is not specified, it is assumed to be the same
-- as T1ColumnList. Otherwise, list the columns of Table2 in
-- the same order as the columns in table1 that you wish to compare.
--
-- The result is all records from either table that do NOT match
-- the other table, along with which table the record is from.
declare #SQL varchar(8000);
IF #t2ColumnList = '' SET #T2ColumnList = #T1ColumnList
set #SQL = 'SELECT ''' + #table1 + ''' AS TableName, ' + #t1ColumnList +
' FROM ' + #Table1 + ' UNION ALL SELECT ''' + #table2 + ''' As TableName, ' +
#t2ColumnList + ' FROM ' + #Table2
set #SQL = 'SELECT Max(TableName) as TableName, ' + #t1ColumnList +
' FROM (' + #SQL + ') A GROUP BY ' + #t1ColumnList +
' HAVING COUNT(*) = 1'
exec ( #SQL)
Would something like this work for you:
select
(Case when log.RecordID = audit.CardID THEN 1 else 0) as RecordIdEqual,
(Case when log.Name = audit.Name THEN 1 else 0) as NamesEqual ,
(Case when log.Date = audit.DeploymentDate THEN 1 else 0) as DatesEqual,
(Case when log.Address = audit.ShippingAddress THEN 1 else 0) as AddressEqual,
(Case when log.Products = audit.Options THEN 1 else 0) as ProductsEqual
--etc
from Audit audit, Log log
where audit.CardID=log.RecordId
This will give you a break down of what's equal based on the column name. Seems like it might be easier than doing all the casting and having to interpret the resulting string...

Use a SELECT to Print a Bunch of INSERT INTOs

I have a bunch of records I want to move to another database and I just want to create a bunch of inserts that I can copy and paste. I've seen someone do this before but I can't figure it out. I'm not getting the escapes right.
It's something like this where 'Code', 'Description' and 'Absent' are the columns I want from the table.
SELECT 'INSERT INTO AttendanceCodes
(Code, Description, Absent)
VALUES
(' + Code + ',' + Description + ',' + Absent')'
FROM AttendanceCodes
The end result should be a slew of INSERTS with the correct values like this:
INSERT INTO AttendanceCodes
(Code, Description, Absent)
VALUES
('A','Unverified Absence','UA')
Check this
SELECT 'INSERT INTO AttendanceCodes (Code, Description, Absent) VALUES (''' + Code + ''',''' + Description + ''',''' + Absent''')'
FROM AttendanceCodes
try something like this:
SELECT
'INSERT INTO AttendanceCodes (Code, Description, Absent) VALUES ('
+ ISNULL(''''+CONVERT(varchar(8000),Code)+'''','null')
+ ','
+ ISNULL(''''+CONVERT(varchar(8000),Description)+'''','null')
+ ','
+ ISNULL(''''+CONVERT(varchar(8000),Absent)+'''','null')
+')'
FROM AttendanceCodes
This will handle NULLs. I'm not sure of your columns data types, but you can only concatenate strings, so I forced everything to varchar(8000), you can modify this as necessary.
For a generic stored procedure that takes a table name (and some other options) and generates insert statements for current data in that table, check out this handy
link.
It creates a procedure called sp_generate_inserts. You pass this procedure the table name, and it will generate all the insert statements to recreate the data.