SQL query with multiple joins a subqueries - sql

I understand that a sql query cannot dynamically add and remove columns.
Every site will have the same extra data.
So what I need to do is get all the data from 'z' and tack on certain fields from linked tables.
Table tt_transaction and Table tt_extendeddatatype are independent.
tt_extendeddatadetail has 2 foreign keys.
1) fk-> tt_transaction.id
2) fk -> tt_extendeddatatype
I need to pull each transaction with its extended data. Each site will have the same extended data names.
I think the following broken code demonstrates what I am trying to do much better than I can explain.
SELECT z.*, aaa.name, aaa.value, bbb.name, bbb.value FROM tt_transaction
LEFT JOIN
(
SELECT a.name, a.description, b.value, b.transaction_id AS text FROM tt_extendeddatatype AS a
INNER JOIN tt_extendeddatadetail AS b ON a.id = b.type_id
WHERE a.name = 'Number'
) AS aaa
ON aaa.transaction_id = z.id
LEFT JOIN
(
SELECT a.name, a.description, b.value, b.transaction_id AS text FROM tt_extendeddatatype AS a
INNER JOIN tt_extendeddatadetail AS b ON a.id = b.type_id
WHERE a.name = 'STRING'
) AS bbb
ON bbb.transaction_id = z.id
My current error is column aaa.transaction_id does not exist

SELECT z.*, aaa.name, aaa.value, bbb.name, bbb.value FROM tt_transaction
LEFT JOIN
(
SELECT a.name, a.description, b.value, b.transaction_id AS transaction_id FROM tt_extendeddatatype AS a
INNER JOIN tt_extendeddatadetail AS b ON a.id = b.type_id
WHERE a.name = 'Number'
) AS aaa
ON aaa.transaction_id = z.id
LEFT JOIN
(
SELECT a.name, a.description, b.value, b.transaction_id AS text FROM tt_extendeddatatype AS a
INNER JOIN tt_extendeddatadetail AS b ON a.id = b.type_id
WHERE a.name = 'STRING'
) AS bbb
ON bbb.transaction_id = z.id
You named the column TEXT instead of transaction_id in your subquery.

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.

What is wrong with the following SQL left join?

I have 5 tables: A, B, C, D, E
A: {ID, Value, GroupID, FilterID, In_Date}
B: {ID, Description, Out_Date, From_Date }
C: {ID, Category}
D: {GroupID, GroupName}
E: {FilterID, FilterName}
There could be missing IDs in B and C as a result, I'm trying to do a LEFT JOIN to get the required info. In my dataset, I've explicitly added a row in A with ID that's not in B or C. I expect it to be picked up in the following query, but it doesn't happen. I believe I'm going wrong in the WHERE clause due to null values in B and C.
SELECT A.Value, A.Group, B.Description, C.Category, D.GroupName, E.FilterName
FROM A LEFT JOIN B ON A.ID = B.ID
LEFT JOIN C ON A.ID = C.ID,
D,E
WHERE
B.Out_Date>A.In_Date,
A.GroupID=D.GroupID,
A.FilterID=E.FilterID
How can I fix this to retrieve the fields I want with null values when the ID is not in B or C , but in A?
1) Don't mix old comma separated join syntax with modern explicit join syntax!
2) When left join, put the right side table's conditions in the ON clause to get true left join behavior. (When in WHERE you get inner join result.)
SELECT A.Value, A.Group, B.Description, C.Category, D.GroupName, E.FilterName
FROM A LEFT JOIN B ON A.ID = B.ID AND B.Out_Date > A.In_Date
LEFT JOIN C ON A.ID = C.ID
JOIN D ON A.GroupID = D.GroupID
JOIN E ON A.FilterID = E.FilterID
Value and Group are reserved words in ANSI SQL, so you may need to delimit them, as "Value" and "Group".
First point: You shouldn't mix join syntaxes, just use explicit syntax.
Because of your where clause, you effectively turn your left joins in inner joins. You are probably looking for:
SELECT A.Value, A.Group, B.Description, C.Category, D.GroupName, E.FilterName
FROM A
LEFT JOIN B
ON A.ID = B.ID
AND B.Out_Date > A.In_Date
LEFT JOIN C
ON A.ID = C.ID
INNER JOIN D
ON A.GroupID = D.GroupID
INNER JOIN E
ON A.FilterID = E.FilterID
You can use this query for your problem.
SELECT A.Value, A.Group, B.Description, C.Category, D.GroupName, E.FilterName
FROM A
LEFT JOIN B
ON A.ID = B.ID
AND B.Out_Date > A.In_Date
LEFT JOIN C
ON A.ID = C.ID
LEFT JOIN D
ON A.GroupID = D.GroupID
LEFT JOIN E
ON A.FilterID = E.FilterID
Here is your table details
A: {ID, Value, GroupID, FilterID, In_Date}
B: {ID, Description, Out_Date, From_Date }
C: {ID, Category}
D: {GroupID, GroupName}
E: {FilterID, FilterName}
now you try to retrieve data using left join
so you try the flowing script
SELECT A.Value, A.Group, B.Description, C.Category, D.GroupName,E.FilterName from A left join B on A.ID=B.ID
left Join C on A.ID=C.ID
Left Join D on A.GroupID=D.GroupID
Left Join E on A.FilterID=E.FilterID
where B.Out_Date>A.In_Date
I hope this is help full for you.

Oracle - Join operation with table aliases

I have 3 Table A B and C. I want to firstly process Table A and B to create a table 'AB' then join the result of that to C. Here is what I have in mind:
select C.number, C.class, AB.name
from C,
(select A.name, A.number
from A
FULL OUTER JOIN B
ON A.number = B.number) AB
FULL OUTER JOIN AB
ON AB.number = C.number
However it doesn't seem to be working. Now I am not sure if giving the result of A&B an alias works in Oracle DB. How should this be done the correct way?
SELECT C.number,
C.class,
AB.name
FROM C
FULL OUTER JOIN
(SELECT A.name,
A.number
FROM A
FULL OUTER JOIN B
ON A.number = B.number) AB
ON AB.number = C.number

Simulate a left join without using "left join"

I need to simulate the left join effect without using the "left join" key.
I have two tables, A and B, both with id and name columns. I would like to select all the dbids on both tables, where the name in A equals the name in B.
I use this to make a synchronization, so at the beginning B is empty (so I will have couples with id from A with a value and id from B is null). Later I will have a mix of couples with value - value and value - null.
Normally it would be:
SELECT A.id, B.id
FROM A left join B
ON A.name = B.name
The problem is that I can't use the left join and wanted to know if/how it is possible to do the same thing.
you can use this approach, but you must be sure that the inner select only returns one row.
SELECT A.id,
(select B.id from B where A.name = B.name) as B_ID
FROM A
Just reverse the tables and use a right join instead.
SELECT A.id,
B.id
FROM B
RIGHT JOIN A
ON A.name = B.name
I'm not familiar with java/jpa. Using pure SQL, here's one approach:
SELECT A.id AS A_id, B.id AS B_id
FROM A INNER JOIN B
ON A.name = B.name
UNION
SELECT id AS A_id, NULL AS B_id
FROM A
WHERE name NOT IN ( SELECT name FROM B );
In SQL Server, for example, You can use the *= operator to make a left join:
select A.id, B.id
from A, B
where A.name *= B.name
Other databases might have a slightly different syntax, if such an operator exists at all.
This is the old syntax, used before the join keyword was introduced. You should of course use the join keyword instead if possible. The old syntax might not even work in newer versions of the database.
I can only think of two ways that haven't been given so far. My last three ideas have already been given (boohoo) but I put them here for posterity. I DID think of them without cheating. :-p
Calculate whether B has a match, then provide an extra UNIONed row for the B set to supply the NULL when there is no match.
SELECT A.Id, A.Something, B.Id, B.Whatever, B.SomethingElse
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN 1
ELSE 0
END Which
FROM A
) A
INNER JOIN (
SELECT 1 Which, B.* FROM B
UNION ALL SELECT 0, B* FROM B WHERE 1 = 0
) B ON A.Which = B.Which
AND (
A.Which = 0
OR (
A.Which = 1
AND A.Id = b.Id
)
)
A slightly different take on that same query:
SELECT A.Id, B.Id
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN A.Id
ELSE -1 // a value that does not exist in B
END PseudoId
FROM A
) A
INNER JOIN (
SELECT B.Id PseudoId, B.Id FROM B
UNION ALL SELECT -1, NULL
) B ON A.Which = B.Which
AND A.PseudoId = B.PseudoId
Only for SQL Server specifically. I know, it's really a left join, but it doesn't SAY LEFT in there!
SELECT A.Id, B.Id
FROM
A
OUTER APPLY (
SELECT *
FROM B
WHERE A.Id = B.Id
) B
Get the inner join then UNION the outer join:
SELECT A.Id, B.Id
FROM
A
INNER JOIN B ON A.name = B.name
UNION ALL
SELECT A.Id, NULL
FROM A
WHERE NOT EXISTS (
SELECT *
FROM B
WHERE A.Id = B.Id
)
Use RIGHT JOIN. That's not a LEFT JOIN!
SELECT A.Id, B.Id
FROM
B
RIGHT JOIN A ON B.name = A.name
Just select the B value in a subquery expression (let's hope there's only one B per A). Multiple columns from B can be their own expressions (YUCKO!):
SELECT A.Id, (SELECT TOP 1 B.Id FROM B WHERE A.Id = B.Id) Bid
FROM A
Anyone using Oracle may need some FROM DUAL clauses in any SELECTs that have no FROM.
You could use subqueries, something like:
select a.id
, nvl((select b.id from b where b.name = a.name), "") as bId
from a
you can use oracle + operator for left join :-
SELECT A.id, B.id
FROM A , B
ON A.name = B.name (+)
Find link :-
Oracle "(+)" Operator
SELECT A.id, B.id
FROM A full outer join B
ON A.name = B.name
where A.name is not null
I'm not sure if you just can't use a LEFT JOIN or if you're restricted from using any JOINS at all. But as far as I understand your requirements, an INNER JOIN should work:
SELECT A.id, B.id
FROM A
INNER JOIN B ON A.name = B.name
Simulating left join using pure simple sql:
SELECT A.name
FROM A
where (select count(B.name) from B where A.id = B.id)<1;
In left join there are no lines in B referring A so 0 names in B will refer to the lines in A that dont have a match
+ or A.id = B.id in where clause to simulate the inner join