How can I make one table that "overrides" another? - sql

Suppose I have two tables:
CREATE TABLE A(
id INT PRIMARY KEY,
x INT,
y INT
)
CREATE TABLE B(
id INT PRIMARY KEY,
x INT,
y INT,
)
Table A contains data brought in from another vendor while table B is our data. For simplicity, I've made these tables be exactly the same in terms of schema, but table B would likely be a superset of table A (it would contain some columns that table A wouldn't in other words).
What I would like to do is create a view C with columns id, x, and y such that the values come from table B unless they're NULL in which case they would come from table A. For instance, suppose I had the following:
INSERT INTO A (id, x, y)
VALUES (1, 2, 3);
INSERT INTO B (id, x, y)
VALUES (1, NULL, NULL);
INSERT INTO A (id, x, y)
VALUES (2, 3, 4);
INSERT INTO B (id, x, y)
VALUES (2, 5, 6);
INSERT INTO A(id, x, y)
VALUES (3, 4, 5);
INSERT INTO B(id, x, y)
VALUES (3, 5, NULL);
So that if I select * from C, I'd get the following rows:
(1, 2, 3)
(2, 5, 6)
(3, 5, 5)
How could I create such a view?

You could join the tables together with a left join, and then select the right columns with a case:
select case when A.x is null then B.x else A.x end
, case when A.y is null then B.y else A.y end
from A
left join
B
on A.id = b.id

Try:
Create view C as
select B.ID,
coalesce(B.x,A.x) x,
coalesce(B.y,A.y) y
from B
left join A
on B.ID = A.ID

Related

How can I achieve to this result with Prisma?

I have this Schema:
one A has many B
one B has many C
How can I have all C for each A ?
in Prisma way or at least in raw sql (I've a little big forgot my sql...)
thanks you!
In the following example, Left Joins seem to be doing what you described
Create Table TableA (id int, something VarChar(20));
Insert Into TableA Values (1, 'A Row No 1');
Create Table TableB (id int, idA Int, something VarChar(20));
Insert Into TableB Values (1, 1, 'B belongs to A');
Create Table TableC (id int, idB Int, something VarChar(25));
Insert Into TableC Values (1, 1, 'C belongs to A1');
Insert Into TableC Values (2, 1, 'C belongs to A1');
Insert Into TableC Values (2, 5, 'does not belong to A1');
Select * From TableA A
Left Join TableB B On B.idA = A.id
Left Join TableC C On C.idB = A.id;
https://dbfiddle.uk/?rdbms=postgres_14&fiddle=f553a8a0a35b4fd6c277a906d1cf1100

Difference between delete statements

DELETE a
FROM TableA a
JOIN TableB b ON a.Field1 = b.Field1 AND a.Field2 = b.Field2;
vs.
DELETE
FROM TableA
WHERE Field1 IN (
SELECT Field1
FROM TableB
) AND Field2 IN (
SELECT Field2
FROM TableB
);
The logical conditions of the two statements are different.
The first statement will delete any row in TableA if both it's Field1 and Field2 correspond to the equivalent columns of a row in TableB.
The second statement will delete any row in TableA if the value of Field1 exists in Field1 of TableB, and the value of Field2 exists in Field2 of TableB - but that doesn't have to be in the same row.
It's easy to see the difference if you change the delete to select.
Here's an example. First, create and populate sample tables (Please save us this step in your future questions):
CREATE TABLE A
(
AInt int,
AChar char(1)
);
CREATE TABLE B
(
BInt int,
BChar char(1)
);
INSERT INTO A (AInt, AChar) VALUES
(1, 'a'), (2, 'a'), (3, 'a'),
(1, 'b'), (2, 'b'), (3, 'b');
INSERT INTO B (BInt, BChar) VALUES
(1, 'a'),
(2, 'b'),
(3, 'c');
The statements (translated to select statements):
SELECT A.*
FROM A
JOIN B
ON AInt = BInt AND AChar = BChar;
SELECT *
FROM A
WHERE AInt IN (
SELECT BInt
FROM B
) AND AChar IN (
SELECT BChar
FROM B
);
Results:
AInt AChar
1 a
2 b
AInt AChar
1 a
2 a
3 a
1 b
2 b
3 b
And you can see a live demo on DB<>Fiddle

Intersecting a dynamic number of tables

I have two relations, a and b, with attributes given by
CREATE TABLE a (id int, b_id int)
CREATE TABLE b (id int)
for which I can assume that all pairs of values in a and all values in b are unique, and which will be based in an SQL Server 2016 database.
A given element of b defines a subset of a.id given by those elements for which the corresponding a.b_id is the given value, and my goal is to produce the intersection of all those subsets.
Say, for instance, that a contains the six values,
INSERT INTO a VALUES (1, 1), (1, 2), (1, 3), (2, 2), (3, 2), (3, 3)
Then the expected results would include the following:
b: (1), (2), (3). Expected result: (1)
b: (1), (2). Expected result: (1)
b: (2). Expected result: (1), (2), (3)
b: (2), (3). Expected result: (1), (3)
b: [Empty]. Expected result: (1), (2), (3)
Using uniqueness, for the case of non-empty b, this can be achieved through
SELECT a.id FROM a
JOIN b on a.b_id = b.id
GROUP BY a.id
HAVING COUNT(a.id) = (SELECT COUNT(*) FROM b)
but this feels clunky, given that SQL has an INTERSECT operator readily available, and were I to write the same query in, say, LINQ, I would simply aggregate intersections. It also fails to produce the desired result in the case of empty b without treating that as a special case.
So, the question becomes: Is there a more idiomatic way of performing the above query, which also works properly for trivial b?
What you are trying to do is called a relational division [1, 2].
CREATE TABLE a (id INT, b_id INT);
CREATE TABLE b (id INT);
INSERT INTO a VALUES
(1, 1), (1, 2), (1, 3),
(2, 2), (3, 2), (3, 3);
DECLARE #i INT = 0;
WHILE #i < 5 BEGIN
TRUNCATE TABLE b;
IF #i = 0 INSERT b VALUES (1), (2), (3);
IF #i = 1 INSERT b VALUES (1), (2);
IF #i = 2 INSERT b VALUES (2);
IF #i = 3 INSERT b VALUES (2), (3);
SELECT DISTINCT x.id
FROM a AS x
WHERE NOT EXISTS (
SELECT *
FROM b AS y
WHERE NOT EXISTS (
SELECT *
FROM a AS z
WHERE z.id = x.id AND z.b_id=y.id
)
)
SET #i = #i + 1;
END;
Test it online.
You could extend your approach to handle empty set case by using:
SELECT a.id
FROM a
LEFT JOIN b on a.b_id = b.id
GROUP BY a.id
HAVING COUNT(b.id) = (SELECT COUNT(*) FROM b);
DBFiddle Demo | DBFiddle Demo - all test cases
Extra: Transient Data (used in second demo)
EDIT:
Another approach to handle empty set and leave INNER JOIN:
SELECT a.id FROM a
JOIN b on a.b_id = b.id
GROUP BY a.id
HAVING COUNT(a.id) = (SELECT COUNT(*) FROM b)
UNION
SELECT a.id
FROM a
WHERE NOT EXISTS (SELECT 1 FROM b);
DBFiddle Demo 3

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

T-Sql Query - Get Unique Rows Across 2 Columns

I have a set of data, with columns x and y. This set contains rows where, for any 2 given values, A and B, there is a row with A and B in columns x and y respectivly and there will be a second row with B and A in columns x and y respectivly.
E.g
**Column X** **Column Y**
Row 1 A B
Row 2 B A
There are multiple pairs of data in
this set that follow this rule.
For every row with A, B in Columns
X and Y, there will always be a
row with B, A in X and Y
Columns X and Y are of type int
I need a T-Sql query that given a set with the rules above will return me either Row 1 or Row 2, but not both.
Either the answer is very difficult, or its so easy that I can't see the forest for the trees, either way it's driving me up the wall.
Add to your query the predicate,
where X < Y
and you can never get row two, but will always get row one.
(This assumes that when you wrote "two given values" you meant two distinct given values; if the two values can be the same, add the predicate where X <= Y (to get rid of all "reversed" rows where X > Y) and then add a distinct to your select list (to collapse any two rows where X == Y into one row).)
In reply to comments:
That is, if currently your query is select foo, x, y from sometable where foo < 3; change it to select foo, x, y from sometable where foo < 3 and x < y;, or for the the second case (where X and Y are not distinct values) select distinct foo, x, y from sometable where foo < 3 and x <= y;.
This should work.
Declare #t Table (PK Int Primary Key Identity(1, 1), A int, B int);
Insert into #t values (1, 2);
Insert into #t values (2, 1);
Insert into #t values (3, 4);
Insert into #t values (4, 3);
Insert into #t values (5, 6);
Insert into #t values (6, 5);
Declare #Table Table (ID Int Primary Key Identity(1, 1), PK Int, A Int, B Int);
Declare #Current Int;
Declare #A Int;
Insert Into #Table
Select PK, A, B
From #t;
Set #Current = 1;
While (#Current <= (Select Max(ID) From #Table) Begin
Select #A = A
From #Table
Where ID = #Current;
If (#A Is Not Null) Begin
Delete From #Table Where B = #A;
If ((Select COUNT(*) From #Table Where A = #A) > 1) Begin
Delete From #Table Where ID = #Current;
End
End
Set #A = Null;
Set #Current = #Current + 1;
End
Select a.*
From #tAs a
Inner Join #Table As b On a.PK = b.PK
SELECT O.X, O.Y
FROM myTable O
WHERE EXISTS (SELECT X, Y FROM myTable I WHERE I.X = O.Y AND I.Y = O.X)
I have not tried this. But, this should work.
To get the highest and lowest of each pair, you could use:
(X+Y+ABS(X-Y)) / 2 as High, (X+Y-ABS(X-Y)) / 2 as Low
So now use DISTINCT to get the pairs of them.
SELECT DISTINCT
(X+Y+ABS(X-Y)) / 2 as High, (X+Y-ABS(X-Y)) / 2 as Low
FROM YourTable