SQL 'arrays' for repeated use with IN comparisons - sql

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

Related

Delete Result Rows from a Table in 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
)

Storing active row in a bit column vs. storing the logic in a view

I have a query where multiple factors determine the actual, active row. Can I do this in real-time and still be performant, or is an approach with a bit field the generally recommended approach, where the currently active field is indicated, index and queried?
My real-time solution involves an intermediate step in a view (temporary table in my example below). Therefore I am concerned about performance, because I will have to deal with hundreds of thousands to millions of records.
To illustrate:
DECLARE #grades TABLE (
person int,
grade int,
attempt int,
correction int)
INSERT #grades VALUES (1, 80, 1, 0)
INSERT #grades VALUES (1, 90, 2, 0)
INSERT #grades VALUES (1, 100, 3, 0)
INSERT #grades VALUES (2, 95, 1, 0)
INSERT #grades VALUES (2, 80, 1, 1)
INSERT #grades VALUES (2, 90, 1, 2)
INSERT #grades VALUES (2, 89, 1, 3)
SELECT b.*
INTO #grades_corrected
FROM #grades AS b
RIGHT JOIN (
SELECT person, attempt, MAX(correction) AS last_correction
FROM #grades as b
GROUP BY person, attempt
)
AS last_corrections
ON (b.attempt = last_corrections.attempt
AND b.correction = last_corrections.last_correction
AND b.person = last_corrections.person
)
SELECT g.*
FROM #grades_corrected g
LEFT OUTER JOIN #grades_corrected g2 ON (
g.person = g2.person
AND g.grade < g2.grade)
WHERE g2.grade is null
DROP TABLE #grades_corrected
Performance is going to be much more nuanced than just the sql you have. In any case the sql you have above will breakdown most quickly on the group by with the max and the temp table copy. Both of these will depend heavily on how many records are in the table and how much power (cpu and ram mostly) on your sql server. If you copy "millions" of records into the temp table, it will most likely be slow by most standards.

Return a constant set of key value pair

Imagine I have something like the following
SELECT 0 AS 'Key','No' AS 'Value'
UNION
SELECT 1 AS 'Key','YES' AS 'Value'
UNION
SELECT 2 AS 'Key','Maybe' AS 'Value'
....
....
How can I make above statement more readable so I can accommodate more constant key/value pair in above list in a single select statement? I don't want to create table variable or create a complex sql statement. Just a single select statement returning bunch of constant key/pair values.
You can use VALUES:
SELECT *
FROM (VALUES
(0, 'No'),
(1, 'Yes'),
(2, 'Maybe')
) t([Key], Value)
Table Value Constructor
Using a table value constructor.
VALUES ((0,'NO'),(1,'YES'),(2,'MAYBE'))
Understand you don't want to create a table variable
I use the accepted answer a lot +1
Just pointing out a table variable lets you declare type and primary key
declare #tbl table ([key] tinyint primary key, [value] varchar(12));
insert into #tbl values (1, 'one')
, (2, 'two')
, (3, 'three');
select * from #tbl order by [key];

SQL Join tables - detecting presence of some tuples but not others

I've got two primary tables: codes and categories.
I've also got a join table code_mappings which associates codes with categories.
I need to be able to determine which codes are mapped to one group of categories, but not mapped to another. Been banging my head against this for a while, but am completely stuck.
Here's the schema:
create table codes(
id int,
name varchar(256));
create table code_mappings(
id int,
code_id int,
category_id int);
create table categories(
id int,
name varchar(256));
And some seed data:
INSERT INTO categories VALUES(1, 'Dental');
INSERT INTO categories VALUES(2, 'Weight');
INSERT INTO categories VALUES(3, 'Other');
INSERT INTO categories VALUES(4, 'Acme Co');
INSERT INTO categories VALUES(5, 'No Name');
INSERT INTO codes VALUES(100, "big bag of cat food");
INSERT INTO codes VALUES(200, "healthy doggie treatz");
INSERT INTO code_mappings VALUES(50, 200, 1);
INSERT INTO code_mappings VALUES(51, 100, 4);
INSERT INTO code_mappings VALUES(52, 100, 3);
How would I write a query that will give me the codes that are mapped to one of categories (1,2,3) but not to one of categories (4,5)?
This is an example of a set-within-sets query. I like to approach these using group by and having, because I find that the most flexible approach:
select cm.code_id
from code_mappings cm
group by cm.code_id
having sum(case when cm.category_id in (1, 2, 3) then 1 else 0 end) = 1 and
sum(case when cm.category_id in (4, 5) then 1 else 0 end) = 0;
Each condition in the having clause implements exactly one of the conditions. You said one code of 1, 2, or 3, hence the = 1 (if you wanted at least one of these three, it would be > 0). You said no 4 or 5, hence = 0.
SELECT *
FROM codes co
WHERE EXISTS (
SELECT *
FROM code_mappings ex
WHERE ex.code_id = co.id
AND ex.category_id IN (1,2,3)
)
AND NOT EXISTS (
SELECT *
FROM code_mappings nx
WHERE nx.code_id = co.id
AND nx.category_id IN (4,5)
)
;

Insert values or ignore row if a constraint fails

In SQL Server 2008, is there a way to insert rows while omitting those rows that cause a foreign key constraint to fail?
E.g. I have an insert statement similar to this:
insert into tblFoo(id, name, parent_id, desc) values
(1, 'a', 1, null),
(2, 'c', 3, 'blah'),
....;
parent_id is a fk to another table. How can I then get sql server to skip rows on which the fk column is invalid?
Update I would like to get this to work automatically, without first having to filter out those rows that violates the fk constraint. The reason for that is because the insert statements are generated by a program so it is not known beforehand which foreign keys exist on each table.
Is a weird situation you got there but you can insert the values to a temporary table and then select only the values with a valid FK.
something like:
declare #tempTable table (
id int,
name nvarchar(50) ,
parent_id int ,
[desc] nvarchar(50)
)
insert into #tempTable values
(1, 'a', 1, null),
(2, 'c', 3, 'blah')
insert into tblFoo(id, name, parent_id, [desc])
select tempTable.* from #tempTable as tempTable
inner join parent_id on parent_id.id = tempTable.parent_id
One way would be to use an Instead Of trigger on Inserts and Updates. You could then evaluate each row being updated before the actual write to the DB takes place. I'm generally not a huge fan of triggers, but you seem to have an unusual requirement here.