Joining 4 tables in SQL - sql

I have a question regarding joining 4 tables. This is my query.
SELECT
a.pick_id, a.Serial_No, a.Work_Ord_No, a.Lot_No,
a.Product_no, a.Plan_Qty, a.Machine_no, a.shift, a.Scan_dt,
b.Trml_code,
case when c.Wire_Type is null then '-' else c.Wire_Type +' '+ c.Wire_Size+' '+c.Wire_Color end as Wire,
case when d.Mtrl_code is null then '-' else d.Mtrl_code end as Material
FROM
pickMaster a
JOIN
pickTerminal b ON b.id = a.id
JOIN
pickWire c ON c.id = a.id
JOIN
pickInserting d ON d.id = a.id
This is the data for 4 table before I join them.
And this is the result after I join them.
The problem is after I join the 4 table the result show 4 row with the same data on material and wire. How to solve this?. Any idea?.

Because, You have double-IDs (103) for the last two tables.

You have a Cartesian product between pickTerminal and pickInserting.
You need to connect this two tables somehow.
Example!! This is the only attribute i see as common?!
SELECT
a.pick_id, a.Serial_No, a.Work_Ord_No, a.Lot_No,
a.Product_no, a.Plan_Qty, a.Machine_no, a.shift, a.Scan_dt,
b.Trml_code,
case when c.Wire_Type is null then '-' else c.Wire_Type +' '+ c.Wire_Size+' '+c.Wire_Color end as Wire,
case when d.Mtrl_code is null then '-' else d.Mtrl_code end as Material
FROM
pickMaster a
JOIN
pickTerminal b ON b.id = a.id
JOIN
pickWire c ON c.id = a.id
JOIN
pickInserting d ON d.id = a.id
and d.mtrl_side = b.Trml_side <-- add this
but without more context its hard to tell how you can archive this.

Since you don`t need any information from the 3rd or 4th tables except the distinct material/ terminal codes , you can use left join instead of join for the last two joins.

Related

Displaying one column from inner query

Please help me solve this:
SELECT a.prs_code,
a.cc,
b.description
FROM idp_inpadoc_prs_cc a,
idp_inpadoc_cat_desc b
WHERE a.ID = b.ID
AND a.prs_code IN (SELECT prs_code
FROM tls221_inpadoc_prs c,
tls201_appln d
WHERE c.appln_id = d.appln_id
AND c.appln_id IN ( '1', '2' ));
In this query, along with prs_code, cc, description, I also want to display the corresponding appln_id. How can I do this? Kindly help. Thanks! :)
so when you start looking at transforming this to joins this should do the same as your query because appln_id is on both the tls221_inadoc_prs and the tsl201_appln tables so you don't actually need the later table if the tls221_inadoc_prs.appln_id is a foreign key to tls201_appln.appln_id meaning that if the value is in tls221_inadoc_prs then it also must be in tls201_appln
SELECT a.prs_code,
a.cc,
b.description
,c.appln_id
FROM
idp_inpadoc_prs_cc a
INNER JOIN idp_inpadoc_cat_desc b
ON a.ID = b.ID
INNER JOIN tls221_inpadoc_prs c
ON a.prs_code = c.prs_code
AND d.appln_id IN (1,2)
If it is not a foreign key you can simply add another join as well:
SELECT a.prs_code,
a.cc,
b.description
,c.appln_id
FROM
idp_inpadoc_prs_cc a
INNER JOIN idp_inpadoc_cat_desc b
ON a.ID = b.ID
INNER JOIN tls221_inpadoc_prs c
ON a.prs_code = c.prs_code
AND c.appln_id IN ( '1', '2' )
INNER JOIN tls201_appln d
ON c.appln_id = d.appln_id

SQL select results not appearing if a value is null

I am building a complex select statement, and when one of my values (pcf_auto_key) is null it will not disipaly any values for that header entry.
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes, h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h, companies c, project_status s, project_classification l
where exists
(select company_name from companies where h.cmp_auto_key = c.cmp_auto_key)
and exists
(select status_code from project_status s where s.pjs_auto_key = h.pjs_auto_key)
and exists
(select classification_code from project_classification where h.pcf_auto_key = l.pcf_auto_key)
and pjm_auto_key = 11
--and pjt_auto_key = 10
and c.cmp_auto_key = h.cmp_auto_key
and h.pjs_auto_key = s.pjs_auto_key
and l.pcf_auto_key = h.pcf_auto_key
and s.status_type = 'O'
How does my select statement look? Is this an appropriate way of pulling info from other tables?
This is an oracle database, and I am using SQL Developer.
Assuming you want to show all the data that you can find but display the classification as blank when there is no match in that table, you can use a left outer join; which is much clearer with explicit join syntax:
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes,
h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h
join companies c on c.cmp_auto_key = h.cmp_auto_key
join project_status s on s.pjs_auto_key = h.pjs_auto_key
left join project_classification l on l.pcf_auto_key = h.pcf_auto_key
where pjm_auto_key = 11
and s.status_type = 'O'
I've taken out the exists conditions as they just seem to be replicating the join conditions.
If you might not have matching data in any of the other tables you can make the other inner joins into outer joins in the same way, but be aware that if you outer join to project_status you will need to move the statatus_type check into the join condition as well, or Oracle will convert that back into an inner join.
Read more about the different kinds of joins.

including a condition dynamically based on another condition

I have a query as below
select --------
from table a
left outer join ....c
where
(a.column='123') and (c.column='456')
I would like to
include "(c.column='456')" only when (a.column='123') is not null
how do I do that in a single query ? or do I need to write two separate queries ?
Should be pretty straightforward :
select --------
from table
left outer join....
where (Condition A IS NULL) OR (condition A AND condition B)
UPDATED: For your conditions:
where (a.column is null) or (a.column='123' and c.column='456')
It will include a a row if it's a.column is null or if bot a.column and c.column have valid values.
As I understand your requirement this is the sql you want
select distinct cm.credit_amt,e.credit_lifetime,e.credit_desc,e.icom_code,e.entry_hint,
e.credit_id,e.credit_type_id,e.recontract_period,a.class_desc,a.offer_id,
a.offer_class_id
from rti_retention.package a
left outer join rti_retention.offer_class_credit b on (a.offer_id=b.offer_id
and a.offer_class_id=b.offer_class_id
and a.customer_type_id=b.customer_type_id)
left outer join rti_retention.credit_pre_bundle c on (b.credit_id=c.credit_id)
left outer join rti_retention.credit e on (c.credit_id=e.credit_id)
left outer join rti_retention.credit_mix_amount cm on (cm.credit_id=c.credit_id and cm.prod_mix_id=a.prod_mix_id)
where a.offer_class_id not in (1,2,16)
and a.channel_id=5 and a.customer_type_id=1
and a.offer_id='6055'
and c.prod_mix_id = case when (select count(*)
from rti_retention.credit_pre_bundle c1
where c1.prod_mix_id='1000' ) > 1 then '1000' else c.prod_mix_id end
and e.icom_code is not null
some time there will be some sql syntax errors. due to i havent full data base i wrote sql on mind. cant test it.

SQL query join conditions

I have a query (exert from a stored procedure) that looks something like this:
SELECT S.name
INTO #TempA
from tbl_Student S
INNER JOIN tbl_StudentHSHistory TSHSH on TSHSH.STUD_PK=S.STUD_PK
INNER JOIN tbl_CODETAILS C
on C.CODE_DETL_PK=S.GID
WHERE TSHSH.Begin_date < #BegDate
Here is the issue, the 2nd inner join and corresponding where statement should only happen if only a certain variable (#UseArchive) is true, I don't want it to happen if it is false. Also, in TSHSH certain rows might have no corresponding entries in S. I tried splitting it into 2 separate queries based on #UseArchive but studio refuses to compile that because of the INTO #TempA statement saying that there is already an object named #TempA in the database. Can anyone tell me of a way to fix the query or a way to split the queries with the INTO #TempA statement?
Looks like you're asking 2 questions here.
1- How to fix the SELECT INTO issue:
SELECT INTO only works if the target table does not exist. You need to use INSERT INTO...SELECT if the table already exists.
2- Conditional JOIN:
You'll need to do a LEFT JOIN if the corresponding row may not exist. Try this.
SELECT S.name
FROM tbl_Student S
INNER JOIN tbl_StudentHSHistory TSHSH
ON TSHSH.STUD_PK=S.STUD_PK
LEFT JOIN tbl_CODETAILS C
ON C.CODE_DETL_PK=S.GID
WHERE TSHSH.Begin_date < #BegDate
AND CASE WHEN #UseArchive = 1 THEN c.CODE_DETL_PK ELSE 0 END =
CASE WHEN #UseArchive = 1 THEN S.GID ELSE 0 END
Putting the CASE statement in the WHERE clause and not the JOIN clause will force it to act like an INNER JOIN when #UseArchive and a LEFT JOIN when not.
I'd replace it with LEFT JOIN
LEFT JOIN tbl_CODETAILS C ON #UseArchive = 1 AND C.CODE_DETL_PK=S.GID
You can split the queries and then insert into a temp table easily.
SELECT * INTO #TempA FROM
(
SELECT * FROM Q1
UNION ALL
SELECT * FROM Q2
) T
SELECT S.name
INTO #TempA
from tbl_Student S
INNER JOIN tbl_StudentHSHistory TSHSH
on TSHSH.STUD_PK = S.STUD_PK
INNER JOIN tbl_CODETAILS C
on C.CODE_DETL_PK = S.GID
and #UseArchive = true
WHERE TSHSH.Begin_date < #BegDate
But putting #UseArchive = true in the join in this case is the same as where
Your question does not make much sense to me
So what if TSHSH certain rows might have no corresponding entries in S?
If you want just one of the joins to match
SELECT S.name
INTO #TempA
from tbl_Student S
LEFT OUTER JOIN tbl_StudentHSHistory TSHSH
on TSHSH.STUD_PK = S.STUD_PK
LEFT OUTER JJOIN tbl_CODETAILS C
on C.CODE_DETL_PK = S.GID
and #UseArchive = true
WHERE TSHSH.Begin_date < #BegDate
and ( TSHSH.STUD_PK is not null or C.CODE_DETL_PK id not null )

Join two queries side by side

I need help in joining the two queries below. I am new to Oracle. I went through the posts here but the joins mentioned are for simple queries. Please help me in displaying the result of below two queries side by side.
(select Branch, count(*) as "No of Accounts Opened"
from (SELECT A.CUST_AC_NO,
A.CCY,
A.branch_code Branch,
A.ACY_CURR_BALANCE,
A.AC_OPEN_DATE,
B.CUSTOMER_NAME1,
C.LIMIT_AMOUNT,
D.ACCOUNT_CLASS,
D.DESCRIPTION
FROM STTM_CUST_ACCOUNT A,
STTM_CUSTOMER B,
getm_facility C,
STTM_ACCOUNT_CLASS D,
getm_liab_cust e
WHERE B.CUSTOMER_NO = A.CUST_NO
AND A.ACCOUNT_CLASS = D.ACCOUNT_CLASS
and c.liab_id = e.liab_id
and e.customer_no = b.customer_no
and e.customer_no = a.cust_no
AND B.LIABILITY_NO = e.customer_no
AND RTRIM(C.LINE_CoDe) || LTRIM(TO_CHAR(C.LINE_SERIAL)) =
RTRIM(A.LINE_ID)
AND A.RECORD_STAT = 'O'
and c.record_stat = 'O'
and e.record_stat = 'O'
--AND to_char(A.AC_OPEN_DATE,'YYYY')=:Year
--AND trim(to_char(A.AC_OPEN_DATE,'Month'))=:Month
--AND a.BRANCH_CODE ='001'
AND A.CCY <> 'ZWD'
UNION
SELECT A.CUST_AC_NO,
A.CCY,
Branch_code Branch,
A.ACY_CURR_BALANCE,
A.AC_OPEN_DATE,
B.CUSTOMER_NAME1,
NULL LIMIT_AMOUNT,
D.ACCOUNT_CLASS,
D.DESCRIPTION
FROM STTM_CUST_ACCOUNT A,
STTM_CUSTOMER B,
STTM_ACCOUNT_CLASS D
WHERE B.CUSTOMER_NO = A.CUST_NO
AND A.ACCOUNT_CLASS = D.ACCOUNT_CLASS
AND A.RECORD_STAT = 'O'
--AND to_char(A.AC_OPEN_DATE,'YYYY')=:Year
--AND trim(to_char(A.AC_OPEN_DATE,'Month'))=:Month
--AND BRANCH_CODE ='001'
AND A.CCY <> 'ZWD')
group by Branch
order by Branch) A
The second query is
(select Branch,count(*) as "No of Accounts Closed" from(SELECT
a.branch_code Branch,
A.CUST_AC_NO,
A.CCY,
A.ACY_CURR_BALANCE,
a.maker_id,
a.maker_dt_stamp,
a.checker_id,
A.CHECKER_DT_STAMP,
B.CUSTOMER_NAME1,
C.ACCOUNT_CLASS,
C.DESCRIPTION
FROM
STTMS_CUST_ACCOUNT A,
STTMS_CUSTOMER B,
STTMS_ACCOUNT_CLASS C
WHERE
B.CUSTOMER_NO = A.CUST_NO
AND A.ACCOUNT_CLASS = C.ACCOUNT_CLASS
AND A.RECORD_STAT = 'C'
---AND A.BRANCH_CODE ='001'- :brn
--AND trunc(to_char(A.CHECKER_DT_STAMP,'YYYY'))=:Year
--AND trim(to_char(A.CHECKER_DT_STAMP,'Month'))=:Month
ORDER BY
CUSTOMER_NAME1)group by Branch order by Branch) B
The only common column between these queries is branch so I'm going to assume that you want to the number of accounts closed and opened per branch.
Firstly, these queries have a lot of extraneous information; only select what you need to. Secondly you should really be using explicit joins. They are a lot clearer, remove a obvious point where errors can occur and have been around for a few decades as the SQL standard.
The simple and quick answer to your question would be:
select a.*, b.*
from query1 a
full outer join query2 b
on a.branch = b.branch
I use full outer join as there's no guarantee that the branch in one query exists in the other. I'm not a fan of quick and simple when I can go into detail though....
The best thing to do is to remove your where conditions to a sum(case when...) in your select. This enables you to only scan your tables twice rather than thrice, and then avoid doing a full outer join as you would have to do at the moment.
Please forgive me if I get these joins slightly wrong; I might make a mistake translating them to the explicit join syntax. I've removed some of your where conditions to the join as they should have been there.
As a little side note your worry about your query being "complex" shouldn't be a worry. Essentially, no matter how long your query you can treat it as if it were the simplest possible; just be aware of what data you think should be returned and be ready to investigate if it seems wrong.
select branch
, sum(opened) as "number of accounts opened"
, sum(closed) as "number of accounts closed"
from ( select branch
, sum(case when a.record_stat = 'C' then 1
else 0 end) as closed
, sum(case when a.record_stat = 'O' and a.ccy <> 'zwd' then 1
else 0 end ) as opened
from STTMS_CUST_ACCOUNT A
join STTMS_CUSTOMER B
on B.CUSTOMER_NO = A.CUST_NO
join STTMS_ACCOUNT_CLASS C
on A.ACCOUNT_CLASS = C.ACCOUNT_CLASS
-- include where condition to use indexes (if possible)
where a.record_stat in ('C','O')
group by branch
-- union would imply a distinct, which we don-t want.
union all
select branch
, count(*) as opened
, 0 as closed
from STTM_CUST_ACCOUNT A
join STTM_CUSTOMER B
on B.CUSTOMER_NO = A.CUST_NO
join getm_facility C
on rtrim(C.LINE_CoDe) || ltrim(to_char(C.LINE_SERIAL))
= rtrim(A.LINE_ID)
-- moved from where as this is a join condition
and a.record_stat = c.record_stat
join STTM_ACCOUNT_CLASS D
on A.ACCOUNT_CLASS = D.ACCOUNT_CLASS
join getm_liab_cust e
on c.liab_id = e.liab_id
and e.customer_no = b.customer_no
and e.customer_no = a.cust_no
and b.liability_no = e.customer_no
-- moved from where as this is a join condition
and a.record_stat = e.record_stat
-- only want one number returned so conditions in the where clause
where A.RECORD_STAT = 'O'
and A.CCY <> 'ZWD'
group by branch
)
group by branch
Sorry, I couldn't be bothered to case everything similarly.
Further Reading:
Join (SQL) - Wikipedia
A Visual Explanation of SQL Joins - Coding Horror
In Function - Tech on the Net
select
branches.branch_code,
first_query."No of Accounts Opened",
second_query."No of Accounts Closed"
from
(select distinct branch_code from STTMS_CUST_ACCOUNT) branches
left outer join (
-- first query here
) first_query on branches.branch_code = first_query.Branch
left outer join (
-- second query here
) second_query on branches.branch_code = second_query.Branch
Because you haven't given a separate table for branches we have to do the select distinct in the first alias. If you have another table with just this information, it'd be better to use it instead.
I should add that it seems like your queries could probably be simplified (it doesn't look like you use all the fields you fetch).