Multi join tables with aggregate using main query parameter in subquery - sql

Quick and easy question.
Say I am joining two tables. I have main query and a sub query. The sub query pulls out one extra column for my resultset. LEFT JOIN account for the fact if there are no matching column in table b i still want to get all columns from table a.
select
a.*, b.sumb
from
ta a
left join
(select
b.uid, sum(b.amount) as sumb
from tb b
group by b.uid) b on a.uid = b.uid
where
a.eid = 'value';
Works great. Problem I need to limit the list of results that get summed by the inner query based on per year grouping. Otherwise the query will just sum everything.
Something like that:
select
a.*, b.sumb
from
ta a
left join
(select
b.uid, sum(b.amount) as sumb
from tb b
where b.year = a.year
group by b.uid) b on a.uid = b.uid
where
a.eid = 'value';
Unfortunately this where clause throws an error
The multi-part identifier "a.year" could not be bound.
Can someone with the knowhow point me in the right direction please?

You want an additional join and group by column:
select a.*, b.sumb
from ta a left join
(select b.uid, b.year, sum(b.amount) as sumb
from tb b
group by b.uid, b.year
) b
on a.uid = b.uid and a.year = b.year
where a.eid = 'value';

Related

Pull columns from series of joins in SQL

I am kind of stuck at one problem at my job where I need to pull 2 cols from base table and 1 column from a series of joins.
Please note that, I can not provide real data so I am using dummy column/table names and there are 100s of columns in real project.
Select A.Name,B.Age, D.Sal
From A Left join B on A.iD=B.id and B.Date=CURRENT_DATE
(/* join A and B table and return distinct column which is B.XYZ)
inner join C on C.iD=B.XYZ
(/* join B and C take C.YYY column for next join */)
inner join D on D.id=C.YYY
(/* Take out the D.Sal column from this join */) where A.Dept='IT'
I have written this query but it is taking forever to run because B.XYZ column has a lot of duplicates. how can I get distinct of B.XYZ column from that join.
For Joining Table B, you first get a distinct table of the columns you need from B then join.
SELECT
A.Name,
B.Age,
D.Sal
From A
LEFT JOIN ( -- Instead of all cols (*), just id, Date, Age and xyz might do
SELECT DISTINCT * FROM B
) B ON A.iD = B.id AND B.Date = CURRENT_DATE
--(/* join A and B table and return distinct column which is B.XYZ */)
INNER JOIN C ON C.iD = B.XYZ
--(/*join B and C take C.YYY column for next join */)
INNER JOIN D ON D.id = C.YYY
--(/* Take out the D.Sal column from this join */)
WHERE A.Dept='IT'
You say you get the same rows multifold, because for a b.id, date and age you get the same xyz more than once, or so I understand it.
One option is to join with a subquery that gets the distinct data:
SELECT a.name, b.age, d.sal
FROM a
LEFT JOIN
(
SELECT DISTINCT id, date, age, xyz FROM b
) dist_b ON dist_b.id = a.id and dist_b.date = CURRENT_DATE
INNER JOIN c ON c.id = dist_b.xyz
INNER JOIN d ON d.id = c.yyy
WHERE a.dept = 'IT';
Of course you can even move the date condition inside the subquery:
SELECT a.name, b.age, d.sal
FROM a
LEFT JOIN
(
SELECT DISTINCT id, age, xyz FROM b WHERE date = CURRENT_DATE
) dist_b ON dist_b.id = a.id
INNER JOIN c ON c.id = dist_b.xyz
INNER JOIN d ON d.id = c.yyy
WHERE a.dept = 'IT';
Your LEFT OUTER JOIN doesn't work by the way. As you are inner joining the following tables, a match must exists, so your outer join becomes an inner join. For the outer join to work you would have to outer join the following tables, too.

SQL Server : joining/ merging tables

Assume there are three total tables A,B, and C
Each table contains following attributes:
Table A: "date", "id", "tv_sales_amt"
Table B: "date", "id", "newspaper_sales_amt"
Table C: "date", "id", "radio_sales_amt"
Using join operators, I'm trying to achieve a single table view with:
Result table:
"date", "id", "tv_sales_amt", "newspaper_sales_amt", "radio_sales_amt"
Desired look of the result table:
date id tv_sales_amt newspaper_sales_amt radio_sales_amt
--------------------------------------------------------------------
20190101 012C 2000 1850 NULL
20190102 102D 1000 NULL 1300
.
.
.
Here are some queries that I've tried:
Query #1:
SELECT
A.date, A.id, tv_sales_amt, newspaper_sales_amt, radio_sales_amt
FROM A
INNER JOIN B ON A.id = B.id
INNER JOIN C ON A.id = C.id
Using inner inner-join, I get duplicated values, which is understandable but is not what I'm looking for.
Query #2:
SELECT
A.date, A.id, tv_sales_amt, newspaper_sales_amt, radio_sales_amt
FROM A
FULL OUTER JOIN B ON A.id = B.id
FULL OUTER JOIN C ON A.id = C.id
Since inner-join would only return results from Table B and C (newspaper_sales_amt and radio_sales_amt) that intersects with Table A, I tried full-outer-join in the hopes that it would give me overview of entire results, even though it includes null values.
With both options that I've tried, I wasn't able to get the expected result (Desired look described above).
Would someone be able to tell me what I'm doing wrong here?
I'm using the latest version of SQL Server Management Studio.
I do know that there must be lots of null values if I were to take an overview of tv_sales_amt, newspapers_sales_amt, and radio_sales_amt, but currently, there are no null values but with duplicates.
Your description implies that date also should be the part of joins.
select coalesce(a.date, b.date, c.date) [date],
coalesce(a.id, b.id, c.id) id,
a.tv_sales_amt, b.newspaper_sales_amt, c.radio_sales_amt
from tableA a
full join tableB b on a.id = b.id and a.date = b.date
full join tableC c on (c.id = b.id and c.date = b.date) or
(c.id = a.id and c.date = a.date);
EDIT: This is DbFiddle demo. Try removing coalesce() for date or id (removing in only one of them helps to see it better).
EDIT: Maybe this shows the need for coalesce() better:
select coalesce(a.date, b.date, c.date) [date],
a.id idA, b.id idB, c.id idC,
a.tv_sales_amt, b.newspaper_sales_amt, c.radio_sales_amt
from tableA a
full join tableB b on a.id = b.id and a.date = b.date
full join tableC c on (c.id = b.id and c.date = b.date) or
(c.id = a.id and c.date = a.date);
Full outer joins are tricky. I would suggest:
SELECT COALESCE(A.DATE, B.DATE, C.DATE) as date,
COALESCE(A.ID, B.ID, C.ID) as id,
A.tv_sales_amt, B.newspaper_sales_amt, C.radio_sales_amt
FROM A FULL JOIN
B
ON B.id = A.id AND
B.date = A.date FULL JOIN
C
ON C.id = COALESCE(A.id, B.id) AND
C.date = COALESCE(B.date, B.date);
I find that using COALESCE() in the ON clause simplifies adding more conditions. From a performance perspective, both COALESCE() and OR are pretty bad.

Using Summary Data as a Parameter in SQL

I'm new to SQL and am using Access to run queries that Excel can't really handle. Here's the basic design of the query:
SELECT A.ID, A.Description, A.Location, B.ID, B.Quantity, B.Location
FROM A LEFT JOIN B ON A.ID = B.ID
In table B, location is all the same value. I want to retain the left join above, but limit the resulting values in table A to whatever the location value is in column B. In my mind this would be a WHERE clause in which A.Location = max(B.Location) or something like that.
Any ideas?
If you want to limit the resulting values in table A to whatever the location value is in table B, why can't you simply use the join based on location also?
SELECT A.ID, A.Description, A.Location, B.ID, B.Quantity, B.Location
FROM A LEFT JOIN B
ON A.ID = B.ID
AND A.location = B.location
You can use a DMax expression to fetch the duplicated non-Null value of B.Location. And that expression can be used in the WHERE clause to limit A rows to only those with matching [Location]:
SELECT A.ID, A.Description, A.Location, B.ID, B.Quantity, B.Location
FROM A LEFT JOIN B ON A.ID = B.ID
WHERE A.Location = DMax("[Location]", "B");
If you prefer not to use DMax since it is Access-specific, you can do it this way instead:
SELECT A.ID, A.Description, A.Location, B.ID, B.Quantity, B.Location
FROM A LEFT JOIN B ON A.ID = B.ID
WHERE A.Location = (SELECT Max([Location]) FROM B);

SQL Select ISNULL not working

I used the query below to extract the data fro the table but it does not working.
SELECT A.ID, AVG(ISNULL(score,0)) AS sc FROM A
LEFT OUTER JOIN B ON A.ID = B.ID
WHERE A.aClass = '1st'
I wanted it to return all the data from Table A with its corresponding average score and return 0 if there is no score yet. Can anyone help me figure out the problem.
Try this
SELECT A.ID, AVG(ISNULL(B.score,0)) AS sc
FROM A
LEFT OUTER JOIN B ON A.ID = B.ID
WHERE A.aClass = '1st'
GROUP BY A.ID

How can you perform a join when also using a comma-separated list of tables in an SQL select statement?

This is evidently correct syntax in SQL Server:
SELECT a.id, b.name
FROM Table1 a, Table2 b
WHERE a.id = b.fk1
So is this:
SELECT a.id, c.status
FROM Table1 a
JOIN Table3 c ON a.id = c.fk2
But this apparently isn't:
SELECT a.id, b.name, c.status
FROM Table1 a, Table2 b
JOIN Table3 c ON a.id = c.fk2
WHERE a.id = b.fk1
I would NOT normally want to construct a query in the third case's style (and really not the first case's either), but it would probably be the path of least resistence in editing some code that's already been written at my company. Somebody used the first form with five different tables, and I really need to work in a sixth table through a JOIN statement, without taking chances of messing up what they already have. Even though I could re-write their stuff outright if I need to, I would really like to know how to do something like in the third case.
Running the code exactly as-is in the examples, the third case gives me this error message:
The multi-part identifier "a.id" could not be bound.
What is syntactically breaking the third case? What simple fix could be applied? Thanks!
I, likewise, would not recommend doing this. But, you can just change the , to a cross join:
SELECT a.id, b.name, c.status
FROM Table1 a cross join Table2 b
JOIN Table3 c ON a.id = c.fk2
WHERE a.id = b.fk1
This code:
SELECT a.id, b.name, c.status
FROM Table1 a, Table2 b
JOIN Table3 c ON a.id = c.fk2
WHERE a.id = b.fk1
is doing a cross join on a and the result of an inner join on b and c. c cannot access any of the fields in a because the join is being performed on b. what you should do is change your query to:
SELECT a.id, b.name, c.status
FROM Table1 a
inner join Table2 b on a.id = b.fk1
inner JOIN Table3 c ON a.id = c.fk2