Delete Result Rows from a Table in SQL - sql

I have 2 tables named BM_Data1 & BM_Data1_May62019. Both Tables contains the same data as BM_Data1_May62019 is the copy of BM_Data1 table. But BM_Data1 has some extra rows, How can I delete those extra rows from BM_Data1 and make it same like BM_Data1_May62019.
I got the extra rows using the following query.
SELECT * FROM
(SELECT * FROM BM_DATA1
EXCEPT
SELECT * FROM BM_DATA1_MAY62019) a
There are 7803 extra rows, how can I delete them from BM_Data1 table?
Thank You

As you confirmed RECID is common in both table with unique value, you can try this following script
DELETE FROM BM_DATA1
WHERE RECID NOT IN
(
SELECT RECID FROM BM_DATA1_MAY62019
)

Use a MERGE statement with WHEN NOT MATCHED BY SOURCE THEN DELETE.
MERGE works like a JOIN of sorts, and you need to be able to identify which rows are equal. You do this by the ON clause - for you, that would be RECID.
I suggest you run these in a transaction first, so you verify that you only delete the data you intend to - and commit the transaction only when you are sure you have the right configuration. If something is wrong, you can rollback
BEGIN TRANSACTION
MERGE BM_DATA1 AS Target
USING BM_DATA1_MAY62019as Source
ON (Target.RECID = Source.RECID)
WHEN NOT MATCHED BY SOURCE
THEN DELETE;
SELECT * FROM BM_DATA1
-- ROLLBACK TRANSACTION -- Uncomment and use this if it deleted the wrong data
-- COMMIT -- Uncomment and use this if it deleted the right data!

I've included some DDL so you can run this for yourself to help you understand the example. The ID column is the one that is common to both tables and you are removing the rows in A that are not in B
create table #data_a( id int, val int)
create table #data_b( id int, val int)
insert into #data_a select 1, 1
insert into #data_a select 2, 4
insert into #data_a select 3, 5
insert into #data_a select 4, 5
insert into #data_a select 5, 5
insert into #data_a select 6, 5
insert into #data_a select 7, 5
insert into #data_a select 8, 5
insert into #data_b select 1, 1
insert into #data_b select 2, 4
insert into #data_b select 3, 5
insert into #data_b select 4, 5
insert into #data_b select 5, 5
-- delete the extra rows in A
delete a from #data_a as a
left join #data_b as b on a.id = b.id
where b.id is null
-- we can see the rows are no longer in B
select * from #data_a
select * from #data_b
drop table #data_a
drop table #data_b

create table t1(id int, demo int);
create table t2(id int, demo int);
insert into t1 values (1, 1);
insert into t2 values (1, 1);
insert into t1 values (2, 2);
insert into t2 values (2, 2);
insert into t1 values (3, 3);
insert into t2 values (3, 3);
insert into t1 values (4, 4); -- t1 table has some extra rows
insert into t1 values (5, 5); -- t1 table has some extra rows
insert into t1 values (6, 6); -- t1 table has some extra rows
To delete those records from first table which are not second table:
delete from t1 where id not in (select id from t2)

use just delete with a correlated subquery
delete from [BM_DATA1]
where not exists
(select 1
from [BM_DATA1_MAY62019]
where [BM_DATA1_MAY62019].RECID = [BM_DATA1].RECID -- put here here identified column name
)

Related

SQL Query GO Command after every 100th INSERT INTO

I have a query that inserts about 60.000 records in a table, using SQL Server.
The 100th INSERT INTO-statement is followed by a GO-command. In total the query contains about 600 GO-commands. Is that really necessary, or can I just have one GO-command at the end with that many INSERT's?
Sample:
(...)
insert into myTable(T_id,T_key,T_value) VALUES(97,97,97)
insert into myTable(T_id,T_key,T_value, T_comment) VALUES(98,98,98,'hello')
insert into myTable(T_id,T_key,T_value, T_range) VALUES(99,99,99,'1-100')
insert into myTable(T_id,T_key,T_value) VALUES(100,100,100)
go
insert into myTable(T_id,T_key,T_value) VALUES(101,101,102)
insert into myTable(T_id,T_key,T_value, T_comment) VALUES(102,102,102,'next')
(...)
insert into myTable(T_id,T_key,T_value) VALUES(60000,60000,60000)
go
insert into subTable(S_id, S_key, S_ref_T) VALUES(1,'s1',(Select top 1 T_ref from myTable where T_id = 34)
insert into subTable(S_id, S_key, S_ref_T) VALUES(23,'s23',(Select top 1 T_ref from myTable where T_id = 6001)
(...)

Insert 1000 rows in single sql statment or procedure

How should i write a single sql statement or a stored procedure,
To insert 1000 values in 1000 rows and same column with each column having different values (among those 1000)
Here is the query i wrote,
INSERT INTO a_b values
(
(SELECT max(a_b_id) + 1 from a_b),
1111,
(SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4')),
0,
1,
sysdate,
sysdate,
0,
1,
null
);
like say, i have 1000 s_id's i want select them one by one and insert them in one particular column, each time creating a new row.
EX, in first row s_id should be 0 then in second row it should be 1 like that goes on till thousand, attached an image of sample database i am working with.
You can use connect by for this:
INSERT INTO a_b (s_id, col2, col3, ....)
select level, --<< this value will change for every row
1111,
sysdate,
... more columns ...
from dual
connect by level <= 1000;
you can use cross apply to get 1000 rows along with 1000 other columns to insert 1000 rows as below:
insert into a_b (column names...)
select (max(a_b_id) over()) +1 as MaxId, s_id from a_b a cross apply (select 0, 1,SYSDATETIME, SYSDATETIME, 0, 1, null) b where a.s_id('111','222')--condition
The below is a syntax error. You will never get something like that to work.
create table fff
( id int not null
);
insert fff values (select 1,7777,select 3, select 3);
So you need to break it up into chunks
DROP PROCEDURE IF EXISTS uspRunMe;
DELIMITER $$
CREATE PROCEDURE uspRunMe()
BEGIN
insert into a_b select max(a_b_id) + 1 from a_b;
insert into a_b values (1111);
insert into a_b SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4');
insert into a_b values (0,1);
insert into a_b select sysdate,sysdate;
insert into a_b values (0,1,null);
END;$$
DELIMITER ;
Test it:
call uspRunMe();
The above is for MySQL. You have a few db engines tagged here.

SQL Server Merge - Getting matched records to another temp table

I have a MERGE query to update data. In case of no match I am inserting records to source getting the output to a temporary table.
Would it be possible to get the matched records to temporary table as well? Basically to avoid duplication of data in further processing I need to have copy of matched records.
This is my MERGE command:
MERGE Product.ProductHeaderRepository AS t
USING (SELECT GETDATE() as d, c1, c2, c3,
Name FROM Supplier.ProductHeaderImport
WHERE (BatchID = #BatchID) ) AS s
ON dbo.GetProductHeaderId(s.c1,S.c2,S.c3) <0
WHEN NOT MATCHED BY TARGET THEN
INSERT (Name, c1,c2,c3) VALUES (Name, c2,c2,c3)
OUTPUT INSERTED.iD, s.c1, s.c2, s.c3 INTO #TmpTable;
You could create a MATCHED clause that does not change anything and just updates a variable, e.g.
DECLARE #T1 TABLE (A INT, B INT);
DECLARE #T2 TABLE (A INT, B INT);
DECLARE #T3 TABLE (Action VARCHAR(20), A INT, B INT);
INSERT #T1 VALUES (1, 1), (2, 2), (3, 3);
INSERT #T2 VALUES (1, 0), (2, NULL), (4, 0);
DECLARE #I INT; -- VARIABLE TO UPDATE
MERGE #T2 B
USING #T1 A
ON A.A = B.A
WHEN MATCHED THEN
UPDATE SET #I = 1 -- DO NOTHING MEANINGFUL IN THE UPDATE;
WHEN NOT MATCHED BY TARGET THEN
INSERT (A, B) VALUES (A.A, A.B)
OUTPUT $action, ISNULL(inserted.A, deleted.A), ISNULL(inserted.B, deleted.B) INTO #T3;
SELECT *
FROM #T3;
Will return:
Action A B
INSERT 3 3
UPDATE 1 0
UPDATE 2 NULL
So if you add a new column to #TmpTable to store the action you can get your matched rows using:
SELECT *
FROM #TmpTable
WHERE Action = 'UPDATE';
And your new rows using:
SELECT *
FROM #TmpTable
WHERE Action = 'INSERT';

SQL 'arrays' for repeated use with IN comparisons

It's entirely possible to do a SELECT statement like so:
SELECT *
FROM orders
WHERE order_id in (10000, 10001, 10003, 10005);
However, is it possible to create a variable which stores that 'array' (10000, ...) for repeated use in multiple statements like so?
SELECT *
FROM orders
WHERE order_id in #IDarray;
Apologies if this is a painfully simple question - we've all gotta ask them once!
Edit: Hmm, perhaps I should clarify. In my exact situation, I have a load of IDs (let's use the array above as an example) that are hard coded but might change.
These should be re-usable for multiple INSERT statements, so that we can insert things into multiple tables for each of the IDs. Two such end results might be:
INSERT INTO table1 VALUES (10000, 1, 2, 3);
INSERT INTO table1 VALUES (10001, 1, 2, 3);
INSERT INTO table1 VALUES (10003, 1, 2, 3);
INSERT INTO table1 VALUES (10005, 1, 2, 3);
INSERT INTO table2 VALUES (10000, a, b, c);
INSERT INTO table2 VALUES (10001, a, b, c);
INSERT INTO table2 VALUES (10003, a, b, c);
INSERT INTO table2 VALUES (10005, a, b, c);
Obviously here being able to specify the array saves room and also allows it to be changed in one location instead of the INSERTs having to be modified.
With Microsoft SQL Server you could use an table variable:
CREATE TABLE #IDTable(id INT PRIMARY KEY);
-- insert IDs into table
SELECT *
FROM orders o
INNER JOIN #IDTable i ON i.id = o.order_id;
INSERT INTO table2
SELECT id, 1, 2, 3
FROM #IDTable
INSERT INTO table2
SELECT id, 'a', 'b', 'c'
FROM #IDTable
With the use of Declare table variable you can achieve what you want to do.
For example :
Declare #tbl table(orderID int,Orders varchar(max));
insert into #tbl
SELECT * FROM orders WHERE order_id in (10000, 10001, 10003, 10005);
Select orderID ,Orders from #tbl

Select records with order of IN clause

I have
SELECT * FROM Table1 WHERE Col1 IN(4,2,6)
I want to select and return the records with the specified order which i indicate in the IN clause
(first display record with Col1=4, Col1=2, ...)
I can use
SELECT * FROM Table1 WHERE Col1 = 4
UNION ALL
SELECT * FROM Table1 WHERE Col1 = 6 , .....
but I don't want to use that, cause I want to use it as a stored procedure and not auto generated.
I know it's a bit late but the best way would be
SELECT *
FROM Table1
WHERE Col1 IN( 4, 2, 6 )
ORDER BY CHARINDEX(CAST(Col1 AS VARCHAR), '4,2,67')
Or
SELECT CHARINDEX(CAST(Col1 AS VARCHAR), '4,2,67')s_order,
*
FROM Table1
WHERE Col1 IN( 4, 2, 6 )
ORDER BY s_order
You have a couple of options. Simplest may be to put the IN parameters (they are parameters, right) in a separate table in the order you receive them, and ORDER BY that table.
The solution is along this line:
SELECT * FROM Table1
WHERE Col1 IN(4,2,6)
ORDER BY
CASE Col1
WHEN 4 THEN 1
WHEN 2 THEN 2
WHEN 6 THEN 3
END
select top 0 0 'in', 0 'order' into #i
insert into #i values(4,1)
insert into #i values(2,2)
insert into #i values(6,3)
select t.* from Table1 t inner join #i i on t.[in]=t.[col1] order by i.[order]
Replace the IN values with a table, including a column for sort order to used in the query (and be sure to expose the sort order to the calling application):
WITH OtherTable (Col1, sort_seq)
AS
(
SELECT Col1, sort_seq
FROM (
VALUES (4, 1),
(2, 2),
(6, 3)
) AS OtherTable (Col1, sort_seq)
)
SELECT T1.Col1, O1.sort_seq
FROM Table1 AS T1
INNER JOIN OtherTable AS O1
ON T1.Col1 = O1.Col1
ORDER
BY sort_seq;
In your stored proc, rather than a CTE, split the values into table (a scratch base table, temp table, function that returns a table, etc) with the sort column populated as appropriate.
I have found another solution. It's similar to the answer from onedaywhen, but it's a little shorter.
SELECT sort.n, Table1.Col1
FROM (VALUES (4), (2), (6)) AS sort(n)
JOIN Table1
ON Table1.Col1 = sort.n
I am thinking about this problem two different ways because I can't decide if this is a programming problem or a data architecture problem. Check out the code below incorporating "famous" TV animals. Let's say that we are tracking dolphins, horses, bears, dogs and orangutans. We want to return only the horses, bears, and dogs in our query and we want bears to sort ahead of horses to sort ahead of dogs. I have a personal preference to look at this as an architecture problem, but can wrap my head around looking at it as a programming problem. Let me know if you have questions.
CREATE TABLE #AnimalType (
AnimalTypeId INT NOT NULL PRIMARY KEY
, AnimalType VARCHAR(50) NOT NULL
, SortOrder INT NOT NULL)
INSERT INTO #AnimalType VALUES (1,'Dolphin',5)
INSERT INTO #AnimalType VALUES (2,'Horse',2)
INSERT INTO #AnimalType VALUES (3,'Bear',1)
INSERT INTO #AnimalType VALUES (4,'Dog',4)
INSERT INTO #AnimalType VALUES (5,'Orangutan',3)
CREATE TABLE #Actor (
ActorId INT NOT NULL PRIMARY KEY
, ActorName VARCHAR(50) NOT NULL
, AnimalTypeId INT NOT NULL)
INSERT INTO #Actor VALUES (1,'Benji',4)
INSERT INTO #Actor VALUES (2,'Lassie',4)
INSERT INTO #Actor VALUES (3,'Rin Tin Tin',4)
INSERT INTO #Actor VALUES (4,'Gentle Ben',3)
INSERT INTO #Actor VALUES (5,'Trigger',2)
INSERT INTO #Actor VALUES (6,'Flipper',1)
INSERT INTO #Actor VALUES (7,'CJ',5)
INSERT INTO #Actor VALUES (8,'Mr. Ed',2)
INSERT INTO #Actor VALUES (9,'Tiger',4)
/* If you believe this is a programming problem then this code works */
SELECT *
FROM #Actor a
WHERE a.AnimalTypeId IN (2,3,4)
ORDER BY case when a.AnimalTypeId = 3 then 1
when a.AnimalTypeId = 2 then 2
when a.AnimalTypeId = 4 then 3 end
/* If you believe that this is a data architecture problem then this code works */
SELECT *
FROM #Actor a
JOIN #AnimalType at ON a.AnimalTypeId = at.AnimalTypeId
WHERE a.AnimalTypeId IN (2,3,4)
ORDER BY at.SortOrder
DROP TABLE #Actor
DROP TABLE #AnimalType
ORDER BY CHARINDEX(','+convert(varchar,status)+',' ,
',rejected,active,submitted,approved,')
Just put a comma before and after a string in which you are finding the substring index or you can say that second parameter.
And first parameter of CHARINDEX is also surrounded by , (comma).