TSQL procedural issue and advice - sql-server

I am looking for advice on the best way to accomplish the following
I have a table in SQL Server that holds downloaded data from an external system. I need to use it to update another database. Some records will be inserts and others will be updates. There is a comment table and a main table to insert/update. Comments are linked by an ID created in the comments table and stored in a column of the main table record. (one to one relationship)
So insert into comment table and get a scope_identity return value and then use that as part of the insert statement for the main table.
The updates get the comment ID from the record in the main table and then update the comment table where necessary and also the main table where necessary
EG Table has 5 records
Get first record
If exists in database
get commentID column from comment table and update comment and main table
If not exists
insert into comment table and return comment ID and insert the record into the main table with that comment ID
get the next record
I'm struggling to figure out how to best do this in SQL Server. Can't find the right combination of cursor, while loops, stored procedure etc. Haven't done much by way of procedural work in SQL Server.
Any advice/help is greatly appreciated
Thanks
Habo. I appreciate the feedback. I do struggle to write a clear concise question. The linked page provides good advice. Hope this script below helps clarify.
Thanks again.
USE TEMPDB
--TABLE TO HOLD JOB RECORDS
create table tbl_jobs(
jobnumber varchar(16) primary key clustered,
jobdesc varchar(50),
commentID int
)
GO
INSERT INTO tbl_jobs VALUES ('Job1','Desc1', '1')
INSERT INTO tbl_jobs VALUES ('Job2','Desc2', '2')
INSERT INTO tbl_jobs VALUES ('Job3','Desc3', '3')
--TABLE TO HOLD JOB RECORD COMMENTS
create table tbl_jobComments(
commentID INT IDENTITY(1,1) NOT NULL,
comment text
)
GO
Insert into tbl_jobComments VALUES ('Comment1')
Insert into tbl_jobComments VALUES ('Comment2')
Insert into tbl_jobComments VALUES ('Comment3')
--TABLE TO HOLD RECORDS DOWNLOADED FROM EXTERNAL SYSTEM
create table tbl_updates(
jobnumber varchar(16) primary key clustered,
jobdesc varchar(50),
comment text
)
GO
INSERT INTO tbl_updates VALUES ('Job1','Desc1Modified', 'Comment1')
INSERT INTO tbl_updates VALUES ('Job2','Desc2', 'Comment2')
INSERT INTO tbl_updates VALUES ('Job3','Desc3Modified', 'Comment3')
INSERT INTO tbl_updates VALUES ('Job4','Desc4', 'Comment4')
GO
--OUTPUT FROM tbl_Jobs
+-----------+---------+-----------+
| jobnumber | jobdesc | commentID |
+-----------+---------+-----------+
| Job1 | Desc1 | 1 |
| Job2 | Desc2 | 2 |
| Job3 | Desc3 | 3 |
+-----------+---------+-----------+
--OUTPUT FROM tbl_JobComments
+-----------+----------+
| commentID | comment |
+-----------+----------+
| 1 | Comment1 |
| 2 | Comment2 |
| 3 | Comment3 |
+-----------+----------+
--OUTPUT FROM tbl_updates
+-----------+---------------+-----------+
| jobnumber | jobdesc | comment |
+-----------+---------------+-----------+
| Job1 | Desc1Modified | Comment1 |
| Job2 | Desc2 | Comment2a |
| Job3 | Desc3Modified | Comment3 |
| Job4 | Desc4 | Comment4 |
+-----------+---------------+-----------+
--DESIRED RESULTS tbl_jobs
+-----------+-----------------+-----------+
| jobnumber | jobdesc | commentID |
+-----------+-----------------+-----------+
| Job1 | Desc1Modified | 1 |
| Job2 | Desc2 | 2 |
| Job3 | Desc3Modified | 3 |
| Job4 | Desc4 | 4 |
+-----------+---------+-------------------+
--DESIRED RESULTS tbl_jobs_comments
+-----------+-----------+
| commentID | comment |
+-----------+-----------+
| 1 | Comment1 |
| 2 | Comment2a |
| 3 | Comment3 |
| 4 | Comment4 |
+-----------+-----------+

You can break this into 2 statements, an update and an insert query
(This assumes there is only 1 comment per ID)
UPDATE maintable
SET Comment=upd.comment
FROM maintable mt
JOIN updatestable upd
ON mt.id=upd.id
then insert what is missing:
INSERT INTO maintable (id,comment)
SELECT id, comment
FROM updatestable
WHERE id NOT IN (SELECT id FROM maintable)

Related

Create SQL Server Select/Delete Query from value in other table

I have a master table named Master_Table and the columns and values in the master table are below:
| ID | Database | Schema | Table_name | Common_col | Value_ID |
+-------+------------+--------+-------------+------------+----------+
| 1 | Database_1 | Test1 | Test_Table1 | Test_ID | 1 |
| 2 | Database_2 | Test2 | Test_Table2 | Test_ID | 1 |
| 3 | Database_3 | Test3 | Test_Table3 | Test_ID2 | 2 |
I have another Value_Table which consist of values that need to be deleted.
| Value_ID | Common_col | Value |
+----------+------------+--------+
| 1 | Test_ID | 110 |
| 1 | Test_ID | 111 |
| 1 | Test_ID | 115 |
| 2 | Test_ID2 | 999 |
I need to build a query to create a SQL query to delete the value from the table provided in Master_Table whose database and schema information is provided in the same row. The column that I need to refer to delete the record is given in Common_col column of master table and the value I need to select is in Value column of Value_Table.
The result of my query should create a query as given below :
DELETE FROM Database_1.Test1.Test_Table1 WHERE Test_ID=110;
or
DELETE FROM Database_1.Test1.Test_Table1 WHERE Test_ID in (110,111,115);
These query should be inside a loop so that I can delete all the row from all the database and tables provided in master table.
Queries don't really create queries.
One way to do what you're saying, which could be useful if this is a one time thing or very occasional thing, is to use SSMS to generate query statements, then copy them to the clipboard, paste them into the window, and execute there.
SELECT 'DELETE FROM Database_1.Test1.Test_Table1 WHERE '
+ common_col
+ ' = '
+ convert(VARCHAR(10),value)
This probably isn't what you want; it sounds more like you want to automate cleanup or something.
You can turn this into one big query if you don't mind repeating yourself a little:
DELETE T1
FROM Database_1.Test1.Test_Table1 T1
INNER JOIN Database_1.Test1.ValueTable VT ON
(VT.common_col = 'Test_ID' and T1.Test_ID=VT.Value) OR
(VT.common_col = 'Test_ID2' and T1.Test_ID2=VT.Value)
You can also use dynamic SQL combined with the first part ... but I hate dynamic SQL so I'm not going to put it in my answer.

Troubleshooting to implement SQL Server trigger

I have this table called InspectionsReview:
CREATE TABLE InspectionsReview
(
ID int NOT NULL AUTO_INCREMENT,
InspectionItemId int,
SiteId int,
ObjectId int,
DateReview DATETIME,
PRIMARY KEY (ID)
);
Here how the table looks:
+----+------------------+--------+-----------+--------------+
| ID | InspectionItemId | SiteId | ObjectId | DateReview |
+----+------------------+--------+-----------+--------------+
| 1 | 3 | 3 | 3045 | 20-05-2016 |
| 2 | 5 | 45 | 3025 | 01-03-2016 |
| 3 | 4 | 63 | 3098 | 05-05-2016 |
| 4 | 5 | 5 | 3041 | 03-04-2016 |
| 5 | 3 | 97 | 3092 | 22-02-2016 |
| 6 | 1 | 22 | 3086 | 24-11-2016 |
| 7 | 9 | 24 | 3085 | 15-12-2016 |
+----+------------------+--------+-----------+--------------+
I need to write trigger that checks before the new row is inserted to the table if the table already has row with columns values 'ObjectId' and 'DateReview' that equal to the columns values of the row that have to be inserted, if it's equal I need to get the ID of the exited row and to put to trigger variable called duplicate .
For example, if new row that has to be inserted is:
INSERT INTO InspectionsReview (InspectionItemId, SiteId, ObjectId, DateReview)]
VALUES (4, 63, 3098, '05-05-2016');
The duplicate variable in SQL Server trigger must be equal to 3.
Because the row in InspectionsReview table were ID = 3 has ObjectId and DateReview values the same as in new row that have to be inserted. How can I implement this?
With the extra assumption that you want to log all the duplicate to a different table, then my solution would be to create an AFTER trigger that would check for the duplicate and insert it into your logging table.
Of course, whether this is the solution depends on whether my extra assumption is valid.
Here is my logging table.
CREATE TABLE dbo.InspectionsReviewLog (
ID int
, ObjectID int
, DateReview DATETIME
, duplicate int
);
Here is the trigger (pretty straightforward with the extra assumption)
CREATE TRIGGER tr_InspectionsReview
ON dbo.InspectionsReview
AFTER INSERT
AS
BEGIN
DECLARE #tableVar TABLE(
ID int
, ObjectID int
, DateReview DATETIME
);
INSERT INTO #tableVar (ID, ObjectID, DateReview)
SELECT DISTINCT inserted.ID, inserted.ObjectID, inserted.DateReview
FROM inserted
JOIN dbo.InspectionsReview ir ON inserted.ObjectID=ir.ObjectID AND inserted.DateReview=ir.DateReview AND inserted.ID <> ir.ID;
INSERT INTO dbo.InspectionsReviewLog (ID, ObjectID, DateReview, duplicate)
SELECT ID, ObjectID, DateReview, 3
FROM
#tableVar;
END;

Merge tables having different columns (SQL Server)

I have 2 tables with details as follows
Table 1
Name | City | Employee_Id
-----------------
Raj | CA | A2345
Diya | IL | A1234
Max | PL | A2321
Anna | TX | A1222
Luke | DC | A5643
Table 2
Name | City | Employee_Id | Phone | Age
---------------------------------------
Raj | CA | A2345 | 4094 | 25
Diya | IL | A1234 | 4055 | 19
Max | PL | A2321 | 4076 | 23
As you can see, Employee_Id is the common column in both the columns. I want to update all the entries present in table 1 into table 2.
Raj, Divya and Max are already present in Table 2. So it should not create a duplicate entry in table 2 and skip those 3 entries whereas Anna and Luke are not present in table 2. so this should be added as a new row.
The SQL should be able to merge these 2 columns and ignore the rows which are already present. The final table 2 must be similar to this.
Table 2
Name | City | Employee_Id | Phone | Age
---------------------------------------
Raj | CA | A2345 | 4094 | 25
Diya | IL | A1234 | 4055 | 19
Max | PL | A2321 | 4076 | 23
Anna | TX | A1222 | |
Luke | DC | A5643 | |
Is there a way I could achieve this? I am pretty new to SQL, so any inputs would be of great help. I read about merge and update feature but I guess merge is in Transact-SQL. Also read about joins but could not find a way to crack this.
Demo Setup
CREATE TABLE Table1
([Name] varchar(4), [City] varchar(2), [Employee_Id] varchar(5));
INSERT INTO Table1
([Name], [City], [Employee_Id])
VALUES
('Raj', 'FL', 'A2345'),
('Diya', 'IL', 'A1234'),
('Max', 'PL', 'A2321'),
('Anna', 'TX', 'A1222'),
('Luke', 'DC', 'A5643');
CREATE TABLE Table2
([Name] varchar(4), [City] varchar(2), [Employee_Id] varchar(5), [Phone] int, [Age] int);
INSERT INTO Table2
([Name], [City], [Employee_Id], [Phone], [Age])
VALUES
('Raj', 'CA', 'A2345', 4094, 25),
('Diya', 'IL', 'A1234', 4055, 19),
('Max', 'PL', 'A2321', 4076, 23);
MERGE QUERY
MERGE Table2 AS target
USING Table1 AS source
ON (target.[Employee_Id] = source.[Employee_Id])
WHEN MATCHED THEN
UPDATE SET [Name] = source.[Name],
[City] = source.[City]
WHEN NOT MATCHED THEN
INSERT ([Name], [City], [Employee_Id], [Phone], [Age])
VALUES (source.[Name], source.[City], source.[Employee_Id], NULL, NULL);
SELECT *
FROM Table2

SQL Server : Insert Multiple Rows into Multiple Tables From Table Type Paramater

I'm trying to write a stored procedure which takes in a table type parameter and inserts into two tables at once.
I have an entity table which is a base table holding the id for various tables, below is the entity table and a sample Site table.
------ Entity Table ------------------------------------------
| Id | bigint | NOT NULL | IDENTITY(1,1) | PRIMARY KEY
| TypeId | tinyint | NOT NULL |
| Updated | datetime | NULL |
| Created | datetime | NOT NULL |
| IsActive | bit | NOT NULL |
------- Site Table ---------------------------------------
| EntityId | bigint | NOT NULL | PRIMARY KEY
| ProductTypeCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| SupplierCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| Name | nvarchar(128) | NOT NULL |
| Description | nvarchar(max) | NULL |
And here is my table type used to pass into the stored procedure
------- Site Table Type ----------------------------------
| EntityTypeId | tinyint | NOT NULL |
| ProductTypeCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| SupplierCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| Name | nvarchar(128) | NOT NULL |
| Description | nvarchar(max) | NULL |
The idea is that I will pass in a table type parameter into the stored procedure and insert multiple rows at once to save looping inserting one row at a time.
Here's what I have so far
CREATE PROCEDURE InsertSites
#Sites SiteTypeTable READONLY
AS
BEGIN
-- Insert into Entity & Site Tables here, using the Id from the Entity Table in the Site table
INSERT INTO Entity (TypeId, Updated, Created, IsActive)
OUTPUT [inserted].[Id], S.ProductTypeCode, S.SupplierCode, S.Name, S.Description
INTO Site
SELECT EntityTypeId, NULL, GETDATE(), 1
FROM #Sites S
END
I've read about using insert and output together but cannot get this to work. I've also read about merge but also cannot get this to work.
Any help or pointers you can give will be greatly appreciated.
Thanks
Neil
---- Edit ----
Could I do something like this? I'm not sure how to finish this off...
CREATE PROCEDURE InsertSites
#Sites SiteTypeTable READONLY
AS
BEGIN
-- First insert enough rows into Entity table, saving the inserted Ids to a table variable
DECLARE #InsertedOutput TABLE (EntityId bigint)
INSERT INTO Entity (TypeId, Updated, Created, IsActive)
OUTPUT [inserted].[id]
INTO #InsertedOutput
SELECT EntityTypeId, NULL, GETDATE(), 1
FROM #Sites S
-- Use the Ids in #InsertedOutput against the rows in #Sites to insert into Sites
END

Find primary key from one table in comma separated list

I've been given the task at work of creating a report based on a very poorly designed table structure.
Consider the following two tables. They contain techniques that each person likes to perform at each gym. Keep in mind that a unique person may show up on multiple rows in the PERSONNEL table:
PERSONNEL
+-----+-----+-------+--------+-----------+
| ID | PID | Name | Gym | Technique |
+-----+-----+-------+--------+-----------+
| 1 | 122 | Bob | GymA | 2,3,4 |
+-----+-----+-------+--------+-----------+
| 2 | 131 | Mary | GymA | 1,2,4 |
+-----+-----+-------+--------+-----------+
| 3 | 122 | Bob | GymB | 1,2,3 |
+-----+-----+-------+--------+-----------+
TECHNIQUES
+-----+------------+
| ID | Technique |
+-----+------------+
| 1 | Running |
+-----+------------+
| 2 | Walking |
+-----+------------+
| 3 | Hopping |
+-----+------------+
| 4 | Skipping |
+-----+------------+
What I am having trouble coming up with is a MSSQL query that will reliably give me a listing of every person in the table that is performing a certain technique.
For instance, let's say that I want a listing of every person that likes skipping. The desired results would be:
PREFERS_SKIPPING
+-----+-------+--------+
| PID | Name | Gym |
+-----+-------+--------+
| 122 | Bob | GymA |
+-----+-------+--------+
| 131 | Mary | GymA |
+-----+-------+--------+
Likewise hopping:
PREFERS_HOPPING
+-----+-------+--------+
| PID | Name | Gym |
+-----+-------+--------+
| 122 | Bob | GymA |
+-----+-------+--------+
| 122 | Bob | GymB |
+-----+-------+--------+
I can break out the strings easily in ColdFusion, but that isn't an option due to the size of the PERSONNEL table. Can anyone help?
I think this query looks cleaner:
SELECT p.*,
t.Technique as ParsedTechnique
FROM Personnel p
JOIN Techniques t
ON CHARINDEX((','+CAST(t.id as varchar(10))+','), (','+p.technique+',')) > 0
WHERE t.id ='1';
You can just change the WHERE t.id = to whatever TechniqueId you need.
Fiddle Here
Using this function
Create FUNCTION F_SplitAsIntTable
(
#txt varchar(max)
)
RETURNS
#tab TABLE
(
ID int
)
AS
BEGIN
declare #i int
declare #s varchar(20)
Set #i = CHARINDEX(',',#txt)
While #i>1
begin
set #s = LEFT(#txt,#i-1)
insert into #tab (id) values (#s)
Set #txt=RIGHT(#txt,Len(#txt)-#i)
Set #i = CHARINDEX(',',#txt)
end
insert into #tab (id) values (#txt)
RETURN
END
You can query like this
declare #a Table (id int,Name varchar(10),Kind Varchar(100))
insert into #a values (1,'test','1,2,3,4'),(2,'test2','1,2,3,5'),(3,'test3','3,5')
Select a.ID,Name
from #a a
cross apply F_SplitAsIntTable(a.Kind) b
where b.ID=2
One of the problems you have to prevent is prevent "1" from matching "10" and "11". For this, you want to be sure that all values are delimited by the separator (in this case a comma).
Here is a method using like that should work effectively (although performance will not be so great):
SELECT p.*, t.Technique as ParsedTechnique
FROM Personnel p join
Techniques t
on ','+p.technique+',' like '%,'+cast(t.id as varchar(255))+',%'
WHERE t.id = 1;
If performance is an issue, then fix your data structure an include a PersonTechniques table so you can do a proper join.
The first comment under the question provided the link to the answer. Here's what I ended up going with:
WHERE
p.Technique LIKE '%,29,%' --middle
OR
p.Technique LIKE '29,%' --start
OR
p.Technique LIKE '%,29' --end
OR
p.Technique = '29' --single (good point by Cheran S in comment)
At initial glance I thought it wouldn't work, but clever use of % made it not match ids like 129, etc.

Resources