SELECT JOIN Table Operations - sql

In my query below, I joined tables b, c, d to a by the base_id column.
However, I need to do some operations in my SELECT statement.
Since b, c, and d are not joined to each other,
is my (a.qty - (b.qty - (c.qty + d.qty))) formula computing only those tables that have the same base_id column?
SELECT (a.qty - (b.qty - (c.qty + d.qty))) AS qc_in
FROM receiving a
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM quality_control bb
WHERE location_to = 6
AND is_canceled = 0
GROUP BY base_id
) b
ON b.base_id = a.base_id
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM quality_control ba
WHERE location_from = 6
AND is_canceled = 0
GROUP BY base_id
) c
ON c.base_id = a.base_id
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM issuance
WHERE location_from = 6
AND is_canceled = 0
GROUP BY base_id
) d
ON d.base_id = a.base_id
WHERE a.is_canceled = 0

I think you're confused by how joining works (if I'm reading the question correctly). If you have:
select *
from table1 a
join table2 b
on a.Id = b.Id
join table3c
on a.Id = c.Id
Then yes a is joined to b, and b is joined to c, but that also means that a is joined to c. One way to think of it is as one giant in-memory table that has all of the a columns then all of the b columns and then all of the c columns in the one result.
If a.Id is 1, and you select the row from b where Id is the same (1) and then you join to c where the Id is the same as a (1), then a, b and c all have the same id.
So yes, (a.qty - (b.qty - (c.qty + d.qty))) will only be doing that calculation for rows where a, b, c and d all have the same base_id .

Surely. In the nested statements you join all the tables on base_id. That means, as the result of those joins you will get a huge table containing columns from all of the joined tables with a common column you join on (base_id in your case).

Related

SQL - Count rows based on matching columns and value range

Please see below query using T-SQL with SSMS. There are three tables: B, G and L.
B has a column Bname
G has 2 columns Gname, Gross
L has 2 columns Bname, Gname
Gross column is an INT ranging values between 80 and 100.
Table L's columns: Bname and Gname will feature names from the B and G tables on the same row. Where both names feature on the same row, I would like to COUNT this as one item; only if the Gross on Table G ranges between 80 and 100 to the corresponding Gname row.
My current query reads:
SELECT l.bname, (SELECT COUNT(*) FROM g WHERE g.gross BETWEEN 80 AND 90) AS Good
FROM l
INNER JOIN b
ON b.bname=l.bname
INNER JOIN g
ON g.gname=l.gname
GROUP BY l.bname;
The result is nearly there, but it counts all Table G:Gname rows betweeen 80 and 100. Emitting the instances on Table L where the Bname and Gname are on the same row.
Thanks in advance for looking.
I suspect that you want:
SELECT l.bname,
(SELECT COUNT(*)
FROM b INNER JOIN
g
ON g.gname = l.gname
WHERE b.bname = l.bname AND g.gross BETWEEN 80 AND 90
) AS Good
FROM l ;
The outer aggregation is not needed of l.bname is unique.
This would more commonly be calculating using conditional aggregation:
SELECT l.bname,
SUM(CASE WHEN g.gross BETWEEN 80 AND 90 THEN 1 ELSE 0 END) AS Good
FROM l INNER JOIN
b
ON b.bname = l.bname INNER JOIN
g
ON g.gname = l.gname
GROUP BY l.bname;
No subquery is needed.

SQL Query where the result has duplicate rows

So I have to write a query in SQL where I list the celebrities that have been in relationships with the same celeb. I basically list out celeb1, celeb2, and celeb3 where celeb3 has been in a relationship with both celeb1 and celeb2. Here's the query I'm using:
SELECT S1.Celeb1, S2.Celeb2, S3.name AS Celeb3
FROM Relationships S1, Relationships S2, Celebs S3
WHERE S3.name = S1.Celeb2
AND S3.name = S2.Celeb1
AND S1.Celeb1 <> S2.Celeb2;
It's difficult to know if this query is correct since it gives me 200 rows in the result but I looked at a few of the row and it looks like it's giving me the correct result where celeb3 has been in a relationship with both celeb1 and 2. The problem is that there are duplicate rows in the result. This could be due to the fact that in the relationships table, it lists out the celeb1, celeb2 relationship but it also lists the inverse celeb2, celeb1 as well. So how can I prevent the result from listing out duplicates?
Here are the two tables I'm using to do this (Relationships and Celebs).
CREATE TABLE Celebs(
name VARCHAR(30)
);
CREATE TABLE Relationships (
Celeb1 VARCHAR(30),
Celeb2 VARCHAR(30)
);
Let's look at a sample:
celeb1 celeb2
A B
B C
C D
Expected result:
A and C were both with B.
B and D were both with C.
In order to find these matches I suggest to duplicate tuples such that each couple is twice in the table (if that is not already the case).
celeb1 celeb2
A B
B A
B C
C B
C D
D C
We can already see that B and C each had two partners. Join this data set with itself so as to connect the records.
with rel as
(
select celeb1 as cel1, celeb2 as cel2 from relationships
union
select celeb2 as cel1, celeb1 as cel2 from relationships
)
select rel1.cel2 as celeb1, rel2.cel2 as celeb2, rel1.cel1 as partner
from rel rel1
join rel rel2 on rel2.cel1 = rel1.cel1 and rel2.cel2 > rel1.cel2
order by 1, 2, 3;
If Celeb3 was in a relationship with A and B, you will also get B, A as a result. To avoid that, just make a constraint that A > B:
SELECT DISTINCT S1.Celeb1, S2.Celeb2, S3.name AS Celeb3
FROM Relationships S1, Relationships S2, Celebs S3
WHERE S3.name = S1.Celeb2
AND S3.name = S2.Celeb1
AND S1.Celeb1 > S2.Celeb2
Oracle Setup:
CREATE TABLE celebs ( name ) AS
SELECT 'A' FROM DUAL UNION ALL
SELECT 'B' FROM DUAL UNION ALL
SELECT 'C' FROM DUAL UNION ALL
SELECT 'D' FROM DUAL;
CREATE TABLE relationships ( celeb1, celeb2 ) AS
SELECT 'A', 'B' FROM DUAL UNION ALL
SELECT 'B', 'C' FROM DUAL UNION ALL
SELECT 'C', 'D' FROM DUAL;
Query:
SELECT DISTINCT
c.name,
CASE c.name WHEN r.celeb1 THEN r.celeb2 ELSE r.celeb1 END AS has_relationship_with
FROM celebs c
LEFT OUTER JOIN
relationships r
ON ( c.name = r.celeb1 OR c.name = r.celeb2 );
Output:
NAME HAS_RELATIONSHIP_WITH
---- ---------------------
A B
B A
B C
C B
C D
D C
If you want A,B and do not want the inverse B,A then change the ON clause for the join to:
ON ( ( c.name = r.celeb1 AND c.name < r.celeb2 )
OR ( c.name = r.celeb2 AND c.name < r.celeb1 ) )
Query 2:
You could then group this using LISTAGG to just get one row per person:
SELECT name,
LISTAGG( rel, ',' ) WITHIN GROUP ( ORDER BY rel ) AS has_relationship_with
FROM (
SELECT DISTINCT
c.name,
CASE c.name WHEN r.celeb1 THEN r.celeb2 ELSE r.celeb1 END AS rel
FROM celebs c
LEFT OUTER JOIN
relationships r
ON ( c.name = r.celeb1 OR c.name = r.celeb2 )
)
GROUP BY name;
Output:
NAME HAS_RELATIONSHIP_WITH
---- ---------------------
A B
B A,C
C B,D
D C

Lookup value in second table

I have two tables.
Table Data
ID Item Kvartal
1 Payment 1
2 Salary 2
Table Kvartal
ID Kvartal_text Kvartal_nummer
1 Q1 1
2 Q2 2
I like to map Kvartal in table Data to Kvartal_text in table Kvartal by matching Kvartal in table Data with ID in table Kvartal. To get a result like Payment Q1; Salary Q2.
I have tried
SELECT * FROM Data
WHERE Data.Kvartal IN (SELECT Kvartal.Kvartal_text
FROM Kvartal
WHERE Kvartal.Kvartal_nummer = Data.Kvartal);
Simply JOIN the two tables and select the fields you want:
SELECT d.Item, k.Kvartal_text
FROM Data d
JOIN Kvartal k
ON k.ID = d.Kvartal
You can use MySQL Join operations for such tasks.
SELECT d.ID, d.Item, d.Kvartal, k.Kvartal_text FROM `Data` d
LEFT JOIN(
SELECT Kvartal_text, Kvartal_nummer FROM `Kvartal`
) AS k
ON k.Kvartal_nummer = d.Kvartal

In a left join, select row with value A, if not select row with value B

This must be simple, but I think I'm lost. I have a table A:
name id
Tom 1
Barbara 2
Gregory 3
...and table B:
id nickname preferred
1 Spiderman 0
1 Batman 1
2 Powerpuff 0
3 Donald Duck 0
3 Hulk 1
How do I query the table to get a nickname when it is preferred (1), or any other nickname if preferred is not available.
So the result for Tom would be "Batman", while the result for Barbara would be "Powerpuff".
Just an immediate solution:
select a.id,
b.nickname
from a
join b on a.id = b.id and b.prefered = 1
union all
select a.id,
b.nickname
from a
join b on a.id = b.id and b.prefered = 0
where a.id not in(
select a.id
from a
join b on a.id = b.id and b.prefered = 1
)
Fiddle http://sqlfiddle.com/#!7/0b7db/1
Try below Query:
Which 1. selects row with value A, otherwise, 2. select row with value B
using LEFT JOIN,
SELECT A.name, B.nickname
FROM A
LEFT JOIN
(
SELECT MAX(preferred) AS preferred, id
FROM B
GROUP BY id
)AS B1
ON A.id = B1.id
LEFT JOIN B ON B.preferred = B1.preferred AND B.id = B1.id
If SQLite supported analytic functions then that would provide a fairly clean and convenient solution. No such luck, though. It does simplify the problem that you want either all the preferred nicknames for a given person (of which there will be at most one) or all the non-preferred ones. It is then fairly straightforward to use an inline view to distinguish between those cases and apply a suitable filter:
SELECT p.name, pn.nickname
FROM
person p
JOIN (
SELECT id, MAX(preferred) AS preferred
FROM person_nickname
GROUP BY id
) flag
ON p.id = flag.id
JOIN person_nickname pn
ON pn.id = flag.id AND pn.preferred = flag.preferred

SQL joining two tables with common row

I have 2 tables in sybase
Account_table
Id account_code
1 A
2 B
3 C
Associate_table
id account_code
1 A
1 B
1 C
2 A
2 B
3 A
3 C
I have this sql query
SELECT * FROM account_table account, associate_table assoc
WHERE account.account_code = assoc.account_code
This query will return 7 rows. What I want is to return the rows from associate_table that is only common to the 3 accounts like this:
account id account_code Assoc Id
1 A 1
2 B 1
3 C 1
Can anyone help what kind of join should I do?
SELECT b.id account_id,a.code account_code,a.id assoc_id
FROM associate a,
account b
WHERE a.code = b.code
AND a.id IN (SELECT a.id
FROM associate a,
account b
WHERE a.code = b.code
GROUP BY a.id
HAVING Count(*) = (SELECT Count(*)
FROM account));
NOTE: this query works only if you have unique values in Id and account_code columns in account table. And also, your associate_table should contain unique combination of (id, account,code). i.e., associate table should not contain (1,A) or any pair twice.
Try this
SELECT AC.ID,AC.account_code,ASS.ID
FROM account_table AC INNER JOIN associate_table AS ASS ON AC.account_code = ASS.account_code
OK so far answer is accepted I'll post simpler one:
SELECT *
FROM account_table AS account,
associate_table AS assoc
WHERE account.account_code = assoc.account_code
HAVING (
SELECT
COUNT(*)
FROM associate_table assoc_2
WHERE assoc_2.id = assoc.id
) = 3
here 3 is the number of codes account table has, if it's gonna be dynamic (changing over time),
you can use (SELECT COUNT(*) FROM account_table) instead of exact number. Also I'm sure it will be cached by database engine, so requires less resources