SQL - looking for matches with one to many table - sql

I have two tables, one with orders that looks something like this
Order ID
Coupon
BIN
O1
C1
B1
O2
C2
B3
Coupon
BIN
C1
B1
C1
B2
C2
B2
Now I want to check if the BINs in the first table are not present in the second table against the coupon.
How do I write a Redshift query for this?
For example, my output from the sample tables would be O2, C2, B3

select o.*
from order o
leff join coupon c on o.bin=c.bin and o.coupon=c.coupon
where c.bin is null

An anti-join will find the rows of the first table which BIN value is not present in the second one. For example:
select o.*
from o
leff join c on c.bin = o.bin
where c.bin is null

Related

Select entries that have non repeating values on a specific column (although other columns may have repeating or non repeating values) (SQL)

Let's say I have the following table:
A
B
C
D
a1
b1
c1
d1
a1
b1
c1
d2
a2
b2
c3
d3
a2
b2
c4
d3
I want to filter and see all four columns for entries that have the same value con column A but different on column C, so I get only this as a result:
A
B
C
D
a2
b2
c3
d3
a2
b2
c4
d3
I don't really care if values con columns B and D are the same or different, although I would like to have them in my table to do further analysis later.
Using the DISTINCT statement would give me all the columns as a result, as they all are different in some column, so that doesn't work for me.
I read some questions (like this one) and the answers recommended using the row_number() over(partition by...) clause, although the use they gave it doesn't quite fit my problem (I think), as it would also return the first row with a repeating value on column C.
Any ideas how this could be done?
You can use exists:
select t.*
from t
where exists (select 1
from t t2
where t2.a = t.a and t2.c <> t.c
)
order by t.a;
You could use a self join
select t1.*
from t t1
join t t2 on t1.a=t2.a and t1.c<>t2.c

Inconsistent results with jsonb_array_elements_text() twice in the SELECT list

Why does the behavior of the query below change when the number of elements in the array changes?
The following snippet expands two arrays on the same query and has two different behaviors:
When the two arrays have the same number of elements, one row per
element is returned
When the two arrays have different number of
elements, it behaves like a CROSS JOIN
All of this executed in Postgres 9.5.2:
CREATE TABLE test(a text, b jsonb, c jsonb);
INSERT INTO test VALUES
('A', '["b1","b2"]', '["c1","c2"]'),
('B', '["b1","b2"]', '["c1","c2","c3"]');
SELECT a, jsonb_array_elements_text(b) b, jsonb_array_elements_text(c) c
FROM test;
Here is the result:
A b1 c1
A b2 c2
B b1 c1
B b2 c2
B b1 c3
B b2 c1
B b1 c2
B b2 c3
Here is what I would expect:
A b1 c1
A b1 c2
A b2 c1
A b2 c2
B b1 c1
B b2 c2
B b1 c3
B b2 c1
B b1 c2
B b2 c3
Combining multiple set-returning functions in the SELECT list is not in the SQL standard, where all set-returning elements go into the FROM list. You can do that in Postgres, but it used to exhibit surprising behavior before version 10, where it was finally sanitized.
All of this is not directly related to the datatype jsonb or the function jsonb_array_elements_text() - beyond it being a set-returning function.
If you want the Cartesian product, reliably and not depending on your version of Postgres, use CROSS JOIN LATERAL instead (requires at least Postgres 9.3):
SELECT t.a, jb.b, jc.c
FROM test t
, jsonb_array_elements_text(t.b) jb(b)
, jsonb_array_elements_text(t.c) jc(c)
ORDER BY t.a, ???; -- your desired order seems arbitrary beyond a
The comma in the FROM list (,) is basically short syntax for CROSS JOIN LATERAL here.
See:
What is the difference between LATERAL and a subquery in PostgreSQL?
Explanation for your actual question:
Why does the behavior of the query below change when the number of elements in the array changes?
What is the expected behaviour for multiple set-returning functions in SELECT clause?

Concatenating two tables distributively

I'm not 100% sure how to phrase the question, but I'm pretty much trying to do this:
say I have two tables:
table a:
a1
a2
and
table b:
b1
b2
I want to combine them and create a table such as:
a1 b1
a1 b2
a2 b1
a2 b2
(for every row in table a, create row number of rows in table b sort of)
I figure I'd be able to do this using a loop of some sort, but I was wondering if there was any way to do this with set logic?
The syntax you're looking for is a cross join:
SELECT a.*, b.*
FROM a
CROSS JOIN b
You don't need any loops.
This is a very simple task in SQL.
You can do:
select a.*, b.*
from a
cross join b
or:
select a.*, b.*
from a
inner join b on (1=1)
No need to loop just simple one line query would work.
SELECT a.*, b.* FROM a,b
Note: By Default it is cross join so no need to define keyword cross join.

Finding Permutations of columns in SQL

I have a reference data table having columns as codes and values.
For e.g. there are three code types viz. A, B, C.
The table is as below:
Code Value
---------------------
A1 a_one
A2 a_two
B1 b_one
B2 b_two
B3 b_three
C1 c_one
C2 c_two
C3 c_three
C4 c_four
---------------------
I have a requirement where the input will be code types and output should be all permutations between the input code types.
For e.g. if the input code types are A and C, the output of my sql should be:
col_1 col_2
---------------------
A1 C1
A1 C2
A1 C3
A1 C4
A2 C1
A2 C2
A2 C3
A2 C4
---------------------
Similarly if the input code types is A, B, C, the output of the sql will have three columns with all the permutations between A, B, C viz. A1 B1 C1 to A2 B3 C4.
I have no idea how to start on this. So any hints will be useful.
Thanks for reading!
If I understand your question correctly, this is one of those rare cases where a CROSS JOIN is actually what you want. A CROSS JOIN will give you the Cartesian product of two sets, which means all possible combinations between the values in those sets.
Example:
Table A with column 1 contains values 'a' and 'b'
Table B with column 2 contains values 'c' and 'd'
The following CROSS JOIN query (note there is no 'join condition' specified, on purpose):
SELECT *
FROM A
CROSS JOIN B
will return the following result:
1 2
--------
a c
a d
b c
b d
I created an SQL Fiddle to show you a possible solution. You can tweak it a bit to see if this is what you need. (Note it's an Oracle fiddle, as there is no DB2 option.)

Select full outer join from many-to-many relationships

I am trying to do something in MSSQL which I suppose is a fairly simple and common thing in any database with many-to-many relationships. However I seem to always end up with a quite complicated select query, I seem to be repeating the same conditions several times to get the desired output.
The scenario is like this. I have 2 tables (table A and B) and a cross table with foreign keys to the ID columns of A and B. There can only be one unique pair of As and Bs in the crosstable (I guess the 2 foreign keys make up a primary key in the cross table ?!?). Data in the three tables could look like this:
TABLE A TABLE B TABLE AB
ID Type ID Type AID BID
--------------------------------------------------
R Up 1 IN R 3
S DOWN 2 IN T 3
T UP 3 OUT T 5
X UP 4 OUT Z 6
Y DOWN 5 IN
Z UP 6 OUT
Now let's say I select all rows in A of type UP and all rows in B of type OUT:
SELECT ID FROM A AS A1
WHERE Type = 'UP'
(Result: R, T, X, Z)
SELECT ID FROM B AS B1
WHERE Type = 'OUT'
(Result: 3, 4, 6)
What I want now is to fully outer join these 2 sub queries based on the relations listed in AB. Hence I want all IDs in A1 and B1 to be listed at least once:
A.ID B.ID
R 3
T 3
null 4
X null
Z 6
From this results set I want to be able to see:
- Which rows in A1 does not relate to any rows in B1
- Which rows in B1 does not relate to any rows in A1
- Relations between rows in A1 and B1
I have tried a couple of things such as:
SELECT A1.ID, B1.ID
FROM (
SELECT * FROM A
WHERE Type = 'UP') AS A1
FULL OUTER JOIN AB ON
A1.ID = AB.AID
FULL OUTER JOIN (
SELECT * FROM B
WHERE Type = 'OUT') AS B1
ON AB.BID = B1.ID
This doesn't work, since some of the relations listed in AB are between rows in A1 and rows NOT IN B1 OR between rows in B1 but NOT IN A1.
In other words - I seem to be forced to create a subquery for the AB table also:
SELECT A1.ID, B1.ID
FROM (
SELECT * FROM A
WHERE Type = 'UP') AS A1
FULL OUTER JOIN (
SELECT * FROM AB AS AB1
WHERE
AID IN (SELECT ID FROM A WHERE type = 'UP') AND
BID IN (SELECT ID FROM B WHERE type = 'OUT')
) AS AB1 ON
A1.ID = AB1.AID
FULL OUTER JOIN (
SELECT * FROM B
WHERE Type = 'OUT') AS B1
ON AB1.BID = B1.ID
That just seems like a rather complicated solution for a seemingly simply problem. Especially when you consider that for A1 and B1 subqueries with more (complex) conditions - possible involving joins to other tables (one-to-many) would require the same temporary joins and conditions to be repeated in the AB1 subquery.
I am thinking that there must be an obvious way to rewrite the above select statements in order to avoid having to repeat the same conditions several times. The solution is probably right there in front me, but I just can't see it.
Any help would be appreciated.
I think you could employ a CTE in this case, like this:
;WITH cte AS (
SELECT A.ID AS AID, A.Type AS AType, B.ID AS BID, B.Type AS BType
FROM A FULL OUTER JOIN AB ON A.ID = AB.AID
FULL OUTER JOIN B ON B.ID = AB.BID)
SELECT AID, BID FROM CTE WHERE AType = 'UP' OR BType = 'OUT'
The advantage of using a CTE is that it will be compiled once. Then you can add additional criteria to the WHERE clause outside the CTE
Check this SQL Fiddle