combinations (not permutations) from cross join in sql - sql

If I have a table that I'd like to cross join to itself, how can I remove the duplicate rows? Or to put it another way, how can I do a "order doesn't matter" cross join?
So for example, if I have a table T:
field |
-------
A |
B |
C |
and I cross join to itself so that i don't get the A | A rows
T as t1
cross join
T as t2
on t1.field != t2.field
I would get the following:
field | field
------+-------
A | B
A | C
B | A
B | C
C | A
C | B
However, to me A, B is the same as B, A.
Is there a good way to remove these duplicates? In other words, I want the combinations not the permutations.

T as t1
inner join
T as t2
on t1.field < t2.field
FWIW, you can just use INNER JOIN for this, it's not strictly a CROSS JOIN. MySQL (and perhaps some other RDBMS) treats these two types of join as identical, but in ANSI SQL, a cross join has no join condition -- it's a deliberate Cartesian product.

Related

SQL Join Not Exists

I have the following between these two tables, id column and case column
Left Table Right Table
| id1 | case#1 | | case#1 | id1 |
| id2 | case#1 | | case#1 | id3 |
| id3 | case#2 | | case#2 | id1 |
As you can see case#1 is not assigned to id2 on the right table, therefore I would like to pull the (id2, case#1) record from the left table.
You would need to perform an OUTER JOIN with a multiple JOIN condition:
left_table as l right outer join right_table as r
on l.id = r.id and l.case_ = r.case_
where l.id IS NULL;
This will return non-matching rows from the left table
Have you tried a left join?
LEFT OUTER JOIN table ON table1.field1 = table2.field2
Depends on what you want. If you do INNER JOIN, you get the rows that match in Table1 AND Table2. If you do OUTER JOIN, you get all of Table1 and only the matching rows from Table2.
I've used LEFT JOIN and selected the record with NULL id in the Right Table using WHERE clause. See my query and demo below:
SELECT
*
FROM LeftTable A
LEFT JOIN RightTable B ON A.id=B.id
WHERE B.id IS NULL
DEMO HERE
I would use NOT EXISTS operation in that case.
SELECT l.id, l.value
FROM LeftTable l
WHERE NOT EXISTS (SELECT 1 FROM RightTable r WHERE r.id = l.id and r.case_value = l.case_value)
Efficiency comparing and the difference between NOT EXISTS vs. NOT IN vs. LEFT JOIN WHERE IS NULL are here

Sql : left join exclude values based on another table

I am trying to figure out this without any success i have tried going through several posts but cannot come to a solution. Situation is like this : ( There are no foreign key constraints )
Table A ID | VAL Table B ID|VAL|TEMP RESULT REQUIRED
1 | A 1| A | 2 A
1 | B 1| A | 2 C
1 | C 1| B | 1 D
1 | D 1| C | 2 E
1 | E 1| D | 2 F
1 | F 1| G | 6 H
1 | H
When i run SELECT DISTINCT A.VAL,B.TEMP FROM A
LEFT JOIN B
ON A.VAL=B.VAL AND B.TEMP > 1
This is returning all rows of TABLE A and rows which are common to TABLE B but not validating the condition ( b.val > 1) because since row B is in Table A, it is getting included in the result . However i want that if val b < 2 then it should be not included in result.
I think this is what you are looking for:
select distinct a.val
from a
left outer join b on (a.val = b.val)
where
(b.temp > 1 or b.val is null)
You want to do the test on B.TEMP after doing the outer join. In the where clause, you are testing for two things. Firstly, you a checking that the value of TEMP is greater than 1. This is the condition set forward in the question. The second condition (b.val is null) covers those rows from table A that don't have a corresponding row in table B. For example, rows 'E', 'F' and 'G' in table A don't match to anything in table B, so all the columns of B will be null after the outer join. you might want to look at this link for more information on outer joins: What is the difference between "INNER JOIN" and "OUTER JOIN"?
Howveer, I have noticed that the rows in table B don't need to be unique. You have two rows where VAL= 'A'. What would happen if the TEMP column had different values where one met the condition and the other didn't.
Another option for getting the result might just be to use NOT IN or NOT EXISTS. An example might be:
select * from a
where a.val not in (
select val
from b
where temp < 2
);
If you want to exclude results from the second table, then don't use a left outer join. Just use an inner join:
SELECT DISTINCT A.VAL, B.TEMP
FROM A INNER JOIN
B
ON A.VAL = B.VAL AND B.TEMP > 1;
Or, in your case, you might want B.TEMP < 2.
Add following WHERE clause to your query
WHERE B.Val IS NOT NULL

How to group a set of row from a table by every possible combination of 3 other tables?

Here are the table structures
Table A (id, name)
Table B (id, A-id)
Table C (id, quantity, B-id, D-id)
Table D (id, E-id, F-id)
Table E (id, name)
Table F (id, name)
I want to get the result like this
A.name | E.name | F.name | SUM(C.quantity)
-------------------------------------------
foo | aaa | zzz | 50
-------------------------------------------
foo | aaa | xxx | 0
-------------------------------------------
foo | bbb | www | 10
-------------------------------------------
bar | aaa | zzz | 12
-------------------------------------------
bar | aaa | xxx | 1
-------------------------------------------
bar | bbb | www | 30
-------------------------------------------
I have to show all entries in A, E, and F even though there is no entry related to them in C.
So basically I have to group all data in C by every possible combination of A, E, and F and show 0 if there is no data available in C.
How do I do that?
Make use of CROSS JOIN, LEFT [OUTER] JOIN and COALESCE:
If you want a CROSS JOIN, which is pretty unusual and may produce a lot of rows, it could look like this.
Even more aggressive after update: b and d are also optional now.
SELECT a.name AS a_name, e.name AS e_name, f.name AS f_name
,COALESCE(sum(c.quantity), 0) As sum_quantity
FROM a
CROSS JOIN e
CROSS JOIN f
LEFT JOIN b ON b.a_id = a.id
LEFT JOIN d ON d.e_id = e.id
AND d.f_id = f.id
LEFT JOIN c ON c.b_id = b.id
AND c.d_id = d.id
GROUP BY 1,2,3;
For lack of specification I join c only if both b_id and d_id have a match.
Additional Q in comment
In your last answer you put a parenthesis after cross join, what does
that do?
I quote the manual here:
Use parentheses if necessary to determine the order of nesting. In
the absence of parentheses, JOINs nest left-to-right. In any case JOIN
binds more tightly than the commas separating FROM items.

SQL join problem

I want to retrieve all records from one table when there are no matches in the second table.
So it is kind of the opposite of an inner join.
You need a LEFT JOIN WHERE IS NULL query (aka outer join):
SELECT table1.*
FROM table1
LEFT OUTER JOIN table2
ON table1.id = table2.id
WHERE table2.id IS NULL
Or a NOT IN:
SELECT *
FROM table1
WHERE id NOT IN (SELECT id FROM table2)
You have three options:
Correlated sub-query.
SELECT * FROM TableA
WHERE NOT EXISTS
(SELECT * FROM TableB WHERE TableB.ID = TableA.ID)
Non-correlated sub-query.
SELECT * FROM TableA
WHERE ID NOT IN (SELECT ID FROM TableB)
OUTER JOIN with NULL elimination.
SELECT * FROM TableA
LEFT [OUTER] JOIN TableB
ON TableA.ID = TableB.ID
WHERE TableB.ItsPrimaryKey IS NOT NULL
In the last example some DBMSes require the word OUTER, some permit it, and some do not allow it at all.
Depending on the DBMS, the various options might produce different execution plans and different performance. Select the one with good performance and that best expresses your intent.
It's an outer join:
SELECT *
FROM tableA AS a
LEFT JOIN tableB AS b USING(x)
Say you have:
tableA:
a | x
-----
1 | 1
3 | 3
table B:
b | x
-------
1 | 'a'
2 | 'b'
then the query above will give you
a | b | x
------------
1 | 'a' | 1
3 | NULL | 3
if you want
a | b | x
----------------
1 | 'a' | 1
NULL | 'b' | 2
3 | NULL | 3
you have to use FULL OUTER JOIN instead of LEFT JOIN.
EDIT: As Larry Lustig told me (and I think is correct after rereading the question) the OP does not want any rows from B. So the query should be:
SELECT a.*
FROM tableA AS a
LEFT JOIN tableB AS b USING(x)
WHERE b.x IS NULL
that will yield
a | x
-------
3 | 3
The "opposite" of inner join is called outer join.
Outer join. Technically possibly FULL OUTER JOIN ;)
Adding to the Solution of Johannes Weiß the solution to your other question about finding objects in table a without objects in table b:
SELECT *
FROM tableA AS a
LEFT JOIN tableB AS b
USING(x)
WHERE b.foo IS NULL
You are looking for LEFT JOIN or FULL OUTER JOIN
Not all DBMS support FULL though.
Simulation of it is possible with UNION, example in MySQL:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
I believe you are looking for FULL OUTER JOIN (works with Oracle 9i+).
EDIT: didn't read your question well... LEFT JOIN if you only want NULL values for the second table

Join 2 Tables and display everything

I have 2 simple tables:
table a:
id | txt
---+----
0 | aaa
1 | bbb
2 | ccc
table b:
id | tel
---+----
0 | 000
2 | 111
I am trying to join 2 tables like this:
SELECT a.*,b.*
FROM a,b
WHERE a.id=b.id
It works, but, if there is no entry in the "b" table it wont show anything.
what the sql shows is something like this:
id | txt | tel
---+-----+----
0 | aaa | 000
2 | ccc | 111
I also want to list the "empty" row a.id=1:
id | txt | tel
---+-----+-----
1 | bbb | NULL
Any ideas? Thanks!
(SQL is for MS Access / oledb)
You want a left outer join (or a full outer join, if you want rows in which there is no entry in the a table also).
SELECT a.*,b.* FROM a LEFT OUTER JOIN b ON a.id=b.id
Depending the system, the syntax LEFT JOIN may work as well.
In MSAccess query designer, right click on the relationship connection between the two tables, and edit its properties. Modify it to 'show all records from table a'.
This:
SELECT a.*,b.*
FROM a, b
WHERE a.id = b.id
...is ANSI-89 inner join syntax, and I highly recommend using ANSI-92 syntax instead:
SELECT a.*,
b.*
FROM TABLE_A a
JOIN TABLE_B b ON b.id = a.id
...mostly because ANSI-88 syntax for left joining to tables wasn't consistently implemented on various databases. This will return the results you expect:
SELECT a.id,
a.txt,
b.tel
FROM TABLE_A a
LEFT JOIN TABLE_B b ON b.id = a.id
A LEFT JOIN means you'll get all the records from TABLE_A in this example, and when there is a matching record (based on the ON criteria) in TABLE_B, the value will be displayed. Otherwise, the TABLE_B column references will return NULL values.
For more information on JOINs, see this page for a great pictorial demonstration of what each one represents and how they compare.