combinations (not permutations) from cross join in sql
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.
Cross Join without duplicate combinations
select A.id aid,B.id bid
from A inner join B on a.id <= b.id
union
select B.id,A.id
from A inner join B on b.id < a.id
If you wanted to be more sophisticated:
select distinct
case when a.id<=b.id then a.id else b.id end id1,
case when a.id<=b.id then b.id else a.id end id2
from A cross join B
In my little unscientific bake off with tiny tables, the latter was faster. And below, the case
expressions written as subqueries.
select distinct
(select MIN(id) from (select a.id union select b.id)[ ]) id1,
(select MAX(id) from (select a.id union select b.id)[ ]) id2
from A cross join B
permutation and combination
If by identical you mean pairs like (white, white) perhaps this is what you want:
SELECT a.color, b.color
FROM colors a
CROSS JOIN colors b
WHERE a.color != b.color
If by identical you additionally mean preserve only one of (white, black) or (black, white) perhaps this is what you want:
SELECT a.color, b.color
FROM colors a
CROSS JOIN colors b
WHERE a.color > b.color
The important part is to reject the elements you don't want after you perform the cross-join.
Note that this won't create any new tables or modify existing ones. a
and b
are merely two different aliases for the same table colors
. The table has only one column color
, but since the table is present twice in the SELECT, you need to distinguish both (conceptual, not factual!) instances of the colors
table.
You can't do without a join
(then you'd have too few rows), nor you can easily do without aliases (you have to refer to both columns to reject some rows) nor there is a reason to assign aliases.
Cross join remaining combinations
EDIT:
I thought of another cleaner solution. Do a cross join first, then a right join on the customer_id and product_name, and coalesce the product statuses.
SELECT customer_id, product_name, coalesce(product_status, status_1)
FROM products p
RIGHT JOIN (
SELECT *
FROM (SELECT DISTINCT customer_id FROM products) pro
CROSS JOIN portfolio
) pt
USING (customer_id, product_name)
ORDER BY customer_id, product_name
Old answer:
The idea is to include information of all product names for a customer_id into a list, and check whether the product in portfolio is in that list.
(SELECT customer_id, pt_product_name as product_name, first(status_1) as product_status
FROM (
SELECT
customer_id,
p.product_name as p_product_name,
pt.product_name as pt_product_name,
product_status,
status_1,
status_2,
collect_list(p.product_name) over (partition by customer_id) AS product_list
FROM products p
CROSS JOIN portfolio pt
)
WHERE NOT array_contains(product_list, pt_product_name)
GROUP BY customer_id, product_name)
UNION ALL
(SELECT customer_id, p_product_name as product_name, first(product_status) as product_status
FROM (
SELECT
customer_id,
p.product_name as p_product_name,
pt.product_name as pt_product_name,
product_status,
status_1,
status_2,
collect_list(p.product_name) over (partition by customer_id) AS product_list
FROM products p
CROSS JOIN portfolio pt)
WHERE array_contains(product_list, pt_product_name)
GROUP BY customer_id, product_name)
ORDER BY customer_id, product_name;
which gives
+-----------+------------+--------------+
|customer_id|product_name|product_status|
+-----------+------------+--------------+
| 1| A| Active|
| 1| B| Inelegible|
| 1| C| Ineligible|
| 1| D| Inelegible|
| 2| A| Inelegible|
| 2| B| Active|
| 2| C| Active|
| 2| D| Inelegible|
| 3| A| Cancelled|
| 3| B| Inelegible|
| 3| C| Ineligible|
| 3| D| Inelegible|
+-----------+------------+--------------+
FYI the chunk before UNION ALL
gives:
+-----------+------------+--------------+
|customer_id|product_name|product_status|
+-----------+------------+--------------+
| 1| B| Inelegible|
| 1| C| Ineligible|
| 1| D| Inelegible|
| 2| A| Inelegible|
| 2| D| Inelegible|
| 3| B| Inelegible|
| 3| C| Ineligible|
| 3| D| Inelegible|
+-----------+------------+--------------+
And the chunk after UNION ALL
gives:
+-----------+------------+--------------+
|customer_id|product_name|product_status|
+-----------+------------+--------------+
| 1| A| Active|
| 2| B| Active|
| 2| C| Active|
| 3| A| Cancelled|
+-----------+------------+--------------+
Hope that helps!
SQL: How to generate non-existing combinations
you can use cross join to have all possible combinations, then with the condition, you can remove the already existed ones.
Select * from words a cross join words b
where not exists (select * from combinations c where c.first_id = a.id and c.second_id = b.id)
SQL: self join using each rows only once
Changing the JOIN condition slightly will achieve what you want..
Instead of:
ON (mytable.letter = self.letter and mytable.number != self.number)
use
ON (mytable.letter = self.letter and mytable.number > self.number)
This will only include combinations where self.number
is greater than mytable.number
which in effect restricts the results to one valid ordering of each combination...
SQL Server - all possible combinations of Bit columns
Use a CTE and CROSS JOIN
it with itself 7 times, as in:
with bits as (select 0 as bit union select 1)
select
row_number() over(
order by a.bit, b.bit, c.bit, d.bit, e.bit, f.bit, g.bit) as id,
a.bit, b.bit, c.bit, d.bit, e.bit, f.bit, g.bit
from bits a
cross join bits b
cross join bits c
cross join bits d
cross join bits e
cross join bits f
cross join bits g
Duplicate pairs in SQL cross join?
You can join those p2's
that are CIS differently to those that aren't. One way is as follows.
select
p1.investigator,
p2.investigator
from
proposals AS p1
cross join
proposals AS p2
where
p1.investigatorDepartment = 'CIS' and ((
p2.investigatorDepartment = 'CIS' and
p1.investigator < p2.investigator
) or (
p2.investigatorDepartment != 'CIS' or
p2.investigatorDepartment is null
)) and
p1.proposalNum = p2.proposalNum;
SQL Fiddle
Return all possible combinations of values on columns in SQL
Assuming at least SQL 2005 for the CTE:
;with cteAllColumns as (
select col1 as col
from YourTable
union
select col2 as col
from YourTable
)
select c1.col, c2.col
from cteAllColumns c1
cross join cteAllColumns c2
where c1.col < c2.col
order by c1.col, c2.col
Related Topics
Using a Database Table as a Queue
How to Drop a Default Value or Similar Constraint in T-Sql
Using If Else Statement Based on Count to Execute Different Insert Statements
Determine What User Created Objects in SQL Server
Combine Multiple Select Statements
How to Transform Comma Separated Column into Multiples Rows in Db2
Convert from Date to Epoch-Oracle
Why Is a Primary-Foreign Key Relation Required When We Can Join Without It
SQL Insert into from Multiple Tables
I Need to Know How to Create a Crosstab Query
Grant Privileges for a Particular Database in Postgresql
Select Query with Case Condition and Sum()
Getting Warning: Null Value Is Eliminated by an Aggregate or Other Set Operation
Check Whether String Is a Date Postgresql
Does Assigning Stored Procedure Input Parameters to Local Variables Help Optimize the Query