SQL join exclude results - sql

This is probably something really easy but I cant figure it out at the moment.
Table Order
+----+---------+
| id | name |
+----+---------+
| 1 | Order 1 |
| 2 | Order 2 |
| 3 | Order 3 |
+----+---------+
Table Facturationdetails
+----+----------------+
| id | name |
+----+----------------+
| 1 | Transportation |
| 2 | Regular |
| 3 | Fixed |
+----+----------------+
Table Relation:
Table Facturationdetails
+----------+---------+
| order_id | fact_id |
+----------+---------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
| 2 | 3 |
| 3 | 2 |
+----------+---------+
Now I would like to find out for which order there are no fakturationdetails1(Transportation)
select to.order_id
from table_order to
join table_facturation tf
on tf.order_id = to.order_id
where tf.fakt_id != 1
But this will return all rows:
+---+---+
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
| 2 | 3 |
| 3 | 2 |
+---+---+
And I want the results to be:
Order 2 and Order 3.

I suspect you want to use NOT EXISTS, so rather than finding rows where the fact_id isn't 1, find orders from the table where the fact_id is 1, then exclude those orders:
SELECT o.order_id
FROM table_order o
WHERE NOT EXISTS
( SELECT 1
FROM table_facturation tf
WHERE tf.order_id = o.order_id
AND tf.fakt_id = 1
);
You have not specified a DBMS, but if you are using MySQL you will get better performance using LEFT JOIN\IS NULL:
SELECT o.order_id
FROM table_order o
LEFT JOIN table_facturation tf
ON tf.order_id = o.order_id
AND tf.fakt_id = 1
WHERE tf.order_id IS NULL;

One way to approach this is with a left join and comparison in the where clause. Look for things that match, and then choose those with no match:
select to.order_id
from table_order to left join
table_facturation tf
on tf.order_id = to.order_id and
tf.fakt_id = 1
where tf.fakt_id is null;

Related

SQL join master details table

I have the following two tables in SQL server:
Product:
SELECT Id, Name From Product
x------x--------------------x
| Id | Name |
x------x--------------------x
| 1 | A |
| 2 | B |
| 3 | C |
x------x--------------------x
Order:
SELECT Id, Order_Date, QTY From Order
x------x--------------------x-------x
| Id | Order_Date | QTY |
x------x--------------------x-------x
| 1 | 2014-01-13 | 10 |
| 1 | 2014-01-11 | 15 |
| 1 | 2014-01-12 | 20 |
| 2 | 2014-01-06 | 30 |
| 2 | 2014-01-08 | 40 |
x------x--------------------x-------x
I would like to get a table which contains list of all products and if there is any order placed for the product.
Example:
x------x--------------------x
| Id | Has_Order |
x------x--------------------x
| 1 | 1 |
| 2 | 1 |
| 3 | 0 |
x------x--------------------x
I tried left outer join but it includes all the rows from Order table. What is the most efficient way to write this SQL query?
You can use exists in a subquery:
select p.*,
(case when exists (select 1 from orders o where o.id = p.id)
then 1 else 0
end) as order_exists_flag
from products p;
For performance, you want an index on orders(id). I would expect this to be the fastest approach.
Just another option to consider
Select ID
,Has_Order = max(Flg)
From (
Select ID,Flg = 0 From [Product]
Union All
Select Distinct ID,Flg = 1 From [Order]
) src
Group By ID

How to select records from a table which do not have foreign key in another table

I've got 5 tables in my database
I want to select the V_CODE in V which records do not share any S foreign key with F when F_CODE = 'A'.
I tried something like
select distinct V_CODE
from V
inner join V_S VS on V.V_ID = VS.V_FK
inner join S on VS.S_FK = S.S_ID
where S._ID not in (
select FS.S_FK
from F
inner join F_S on F.F_ID = F_S.F_FK
where F.F_CODE = 'A'
);
but this does not return exactly what I want.
Can someone give a help? Thanks in advance.
Table F
| F_ID | F_CODE |
| 1 | A |
| 2 | B |
| 3 | C |
Table F_S
| F_FK | S_FK |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
Table S
| S_ID | S_CODE |
| 1 | S1 |
| 2 | S2 |
| 3 | S3 |
Table V_S
| V_FK | S_FK |
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 3 |
Table V
| V_ID | V_CODE |
| 1 | V1 |
| 2 | V2 |
| 3 | V3 |
In this case I'd want to return only V2 because it's the only record in table V not sharing a record in Table S when F_CODE = 'A'
If I understand correctly, you want v_codes that have no f_code = 'A' when you follow the relationships among the tables.
To me, this suggests not exists:
select v.*
from v
where not exists (select 1
from v_s join
f_s
on v_s.s_fk = f_s.s_fk join
f
on f_s.f_fk = f.f_id
where f.f_code = 'A' and vs.v_fk = v.v_id
);

Merge columns on two left joins

I have 3 tables as shown:
Video
+----+--------+-----------+
| id | name | videoSize |
+----+--------+-----------+
| 1 | video1 | 1MB |
| 2 | video2 | 2MB |
| 3 | video3 | 3MB |
+----+--------+-----------+
Survey
+----+---------+-----------+
| id | name | questions |
+----+---------+-----------+
| 1 | survey1 | 1 |
| 2 | survey2 | 2 |
| 3 | survey3 | 3 |
+----+---------+-----------+
Sequence
+----+---------+-----------+----------+
| id | videoId | surveyId | sequence |
+----+---------+-----------+----------+
| 1 | null | 1 | 1 |
| 2 | 2 | null | 2 |
| 3 | null | 3 | 3 |
+----+---------+-----------+----------+
I would like to query Sequence and join on both of video and survey tables and merge common columns without specifying the column names (in this case name) like this:
Query Result:
+----+---------+-----------+----------+---------+-----------+-----------+
| id | videoId | surveyId | sequence | name | videoSize | questions |
+----+---------+-----------+----------+---------+-----------+-----------+
| 1 | null | 1 | 1 | survey1 | null | 1 |
| 2 | 2 | null | 2 | video2 | 2MB | null |
| 3 | null | 3 | 3 | survey3 | null | 3 |
+----+---------+-----------+----------+---------+-----------+-----------+
Is this possible?
BTW the below sql doesn't work as it doesn't merge on the name field:
SELECT * FROM "Sequence"
LEFT JOIN "Survey" ON "Survey"."id" = "Sequence"."surveyId"
LEFT JOIN "Video" ON "Video"."id" = "Sequence"."videoId"
This query will show what you want:
select
s.*,
coalesce(y.name, v.name) as name, -- picks the right column
v.videoSize,
y.questions
from sequence s
left join survey y on y.id = s.surveyId
left join video v on v.id = s.videoId
However, the SQL standard requires you to name the columns you want. The only exception being * as shown above.

Create a combined list from two tables

I have a table with CostCenter_ID (int) and a second table with Process_ID (int).
I'd like to combine the results of both tables so that each cost center ID is assigned to all process IDs, like so:
|CostCenterID | ProcessID |
---------------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
I've done it before but I'm drawing a blank. I've tried this:
SELECT CostCenter_ID,NULL FROM dbo.Cost_Centers
UNION ALL
SELECT NULL,Process_ID FROM dbo.Processes
which returns this:
|CostCenterID | ProcessID |
---------------------------
| 1 | NULL |
| NULL | 1 |
| NULL | 2 |
| NULL | 3 |
Try:
select a.CostCenterID, b.ProcessID
from table1 a
cross join table2 b
or:
select a.CostCenterID, b.ProcessID
from table1 a
,table2 b
NB: cross join is the better method as it makes it clearer to the reader what your intentions are.
More info (with pics) here: http://www.w3resource.com/sql/joins/cross-join.php

Finding number of types of accounts from each customer

I am having a lot of trouble with trying to construct a query that will give me the name of each customer and the number of different types of accounts each has. The three types are Checkings, Savings, and CD.
customers:
+--------+--------+
| cid | name |
+--------+--------+
| 1 | a |
| 2 | b |
| 3 | c |
+--------+--------+
accounts:
+-----------+-----------+
| aid | type |
+-----------+-----------+
| 1 | Checkings |
| 2 | Savings |
| 3 | Checkings |
| 4 | CD |
| 5 | CD |
| 6 | Checkings |
+-----------+-----------+
transactions:
+--------+--------+--------+
| tid | cid | aid |
+--------+--------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 4 |
| 5 | 1 | 5 |
| 6 | 3 | 4 |
| 7 | 1 | 6 |
+--------+--------+--------+
The expected answer would be:
a, 3
b, 1
c, 1
Getting the names is simple enough, but how can I keep count of each individual's account as well as compare the accounts to make sure that it is not the same type?
just add DISTINCT inside the COUNT
SELECT a.cid, a.name, COUNT(DISTINCT c.type) totalCount
FROM customers a
INNER JOIN transactions b
ON a.cis = b.cid
INNER JOIN accounts c
ON b,aid = c.aid
GROUP BY a.cid, a.name
Query:
SQLFiddleExample
SELECT
a."name",
COUNT(DISTINCT c."type") totalCount
FROM customers a
INNER JOIN transactions b
ON a."cid" = b."cid"
INNER JOIN accounts c
ON b."aid" = c."aid"
GROUP BY a."cid", a."name"
ORDER BY totalCount DESC
Result:
| NAME | TOTALCOUNT |
---------------------
| a | 3 |
| b | 1 |
| c | 1 |