Find out for missing items across 2 tables - sql

Basically I have following 4 tables (including two lookup tables)
My intention is Basically first needs to find out what are the matching records
between ModTab and MedTab through link keys (ItemID & TrfCode through Lookup1) and then
missing OptCodes in MedTab which are in ModTab and viceversa.
Can I do this with one go
Key fields are the one with same name.
When linking MedTab & Lookup2 needs to use both StateCode and OptCode
ModTab
======
Component
Item ID
MedTab
======
TrfCode
OptCode
StateCode
Lookup1
=======
Item ID
TrfCode
Lookup2
========
Component
StateCode
OptCode
How should I do it
Appreciate your guidence
Cheers
Shabar

I think you can achieve this using a FULL JOIN, although I'm pretty hazy on the exact workings of your schema, somehting along the lines of this should work:
SELECT COALESCE(ModTab.ItemID, MedTab.ItemID) AS ItemID,
COALESCE(ModTab.TrfCode, MedTab.TrfCode) AS TrfCode,
COALESCE(ModTab.Component, MedTab.Component) AS Component,
COALESCE(ModTab.StateCode, MedTab.StateCode) AS StateCode,
COALESCE(ModTab.OptCode, MedTab.OptCode) AS OptCode,
CASE WHEN ModTab.OptCode IS NULL THEN 'MedTab Only'
WHEN MedTab.OptCode IS NULL THEN 'ModTab Only'
ELSE 'Both Tables'
END AS MatchStatus
FROM ( SELECT l1.ItemID,
l1.TrfCode,
l2.Component,
l2.StateCode,
l2.OptCode
FROM ModTab m
INNER JOIN Lookup1 l1
ON l1.ItemID = m.ItemID
INNER JOIN Lookup2 l2
ON l2.Component = m.Component
) ModTab
FULL JOIN
( SELECT l1.ItemID,
l1.TrfCode,
l2.Component,
l2.StateCode,
l2.OptCode
FROM MedTab m
INNER JOIN Lookup1 l1
ON l1.TrfCode = m.TrfCode
INNER JOIN Lookup2 l2
ON l2.StateCode = m.StateCode
AND l2.OptCode = m.OptCode
) MedTab
ON ModTab.ItemID = MedTab.ItemID
AND ModTab.TrfCode = MedTab.TrfCode
AND ModTab.Component = MedTab.Component
AND ModTab.StateCode = MedTab.StateCode
AND ModTab.OptCode = MedTab.OptCode;
Some of the joins may need to be amended, but the principle is there, basically full join the data sets, this will return all the records from each, where one is null you know there record is not in that set, where both are not null you know the record is in both.
EDIT
MS-Access does not support FULL JOIN and has a different JOIN syntax for multiple joins so you will have to use UNION to merge the records, then check where the record source is. Something like this should do it:
SELECT ItemID,
TrfCode,
Component,
StateCode,
OptCode,
IIF(SUM(MedTab)=0,'ModTab',IIF(SUM(ModTab)=0,'MedTab','Both')) AS TabStatus
FROM ( SELECT Lookup1.ItemID,
Lookup1.TrfCode,
Lookup2.Component,
Lookup2.StateCode,
Lookup2.OptCode,
1 AS MedTab,
0 AS ModTab
FROM ( MedTab
INNER JOIN Lookup1
ON Lookup1.TrfCode = MedTab.TrfCode
)
INNER JOIN Lookup2
ON Lookup2.StateCode = MedTab.StateCode
AND Lookup2.OptCode = MedTab.OptCode
UNION ALL
SELECT Lookup1.ItemID,
Lookup1.TrfCode,
Lookup2.Component,
Lookup2.StateCode,
Lookup2.OptCode,
0 AS MedTab,
1 AS ModTab
FROM ( ModTab
INNER JOIN Lookup2
ON ModTab.Component = Lookup2.Component
)
INNER JOIN Lookup1
ON ModTab.ItemID = Lookup1.ItemID
)
GROUP BY ItemID, TrfCode, Component, StateCode, OptCode;
I haven't tested this and it is a while since I did any queries in access so fingers crossed it works first time!

Related

SQL Query to apply join on basis of case Condition

I have a requirement where I need to fetch the Dimension Key of Region table on basis of the following preference.
Fetch dimension key on basis of Zipcode of Physical address(PA)
If the first condition is not satisfied that fetch dimension key on basis of the Zip Code of the Mailing address
If the second condition is also not satisfied than fetch the dimension key on basis of the Parish Code of Physical address
Else fetch dimension key on basis of parish Code of Mailing address.
I am trying to use the below query but is giving multiple records since all left joins are getting evaluated. I want that it should not go on the second condition if the first condition is satisfied.
select REGION_DIM_SK, CASE_NUM
from (
select distinct COALESCE(RDIM.REGION_DIM_SK, RDIM1.REGION_DIM_SK, RDIM2.REGION_DIM_SK, RDIM3.REGION_DIM_SK) AS REGION_DIM_SK
, DC.CASE_NUM, ADDR_TYPE_CD
FROM rpt_dm_ee_intg.CASE_PERSON_ADDRESS dc
left join rpt_dm_ee_prsnt.REGION_DIM RDIM on dc.ZIP_CODE = RDIM.ZIP_CODE and RDIM.REGION_EFF_END_DT IS NULL and dc.addr_type_cd='PA' AND dc.EFF_END_DT IS NULL
left join rpt_dm_ee_prsnt.REGION_DIM RDIM1 ON dc.ZIP_CODE = RDIM1.ZIP_CODE AND RDIM1.REGION_EFF_END_DT IS NULL AND dc.addr_type_cd='MA' AND DC.EFF_END_DT IS NULL
left join (
select PARISH_CD, min(REGION_DIM_SK) as REGION_DIM_SK
from rpt_dm_ee_prsnt.REGION_DIM
where REGION_EFF_END_DT is null
group by PARISH_CD
) RDIM2 ON dc.addr_type_cd='PA' and dc.PARISH_CD = RDIM2.PARISH_CD AND DC.EFF_END_DT IS NULL
left join (
select PARISH_CD, min(REGION_DIM_SK) as REGION_DIM_SK
from rpt_dm_ee_prsnt.REGION_DIM
where REGION_EFF_END_DT is null
group by PARISH_CD
) RDIM3 ON dc.addr_type_cd='MA' and dc.PARISH_CD = RDIM3.PARISH_CD AND DC.EFF_END_DT IS NULL
) A
where REGION_DIM_SK is not null
) RD on RD.case_num = rpt_dm_ee_intg.CASE_PERSON_ELIGIBILITY.CASE_NUM
Use multiple left joins. Your query is rather hard to follow -- it has other tables and references not described in the problem.
But the idea is:
select t.*,
coalesce(rpa.dim_key, rm.dim_key, rpap.dim_key, rmp.dim_key) as dim_key
from t left join
dim_region rpa
on t.physical_address_zipcode = rpa.zipcode left join
dim_region rm
on t.mailing_address_zipcode = rm.zipcode and
rpa.zipcode is null left join
dim_region rpap
on t.physical_addresss_parishcode = rpap.parishcode and
rm.zipcode is null left join
dim_region rmp
on t.physical_addresss_parishcode = rmp.parishcode and
rpap.zipcode is null
The trick is to put the conditions in CASE WHEN:
SELECT *
FROM table1 a
JOIN table2 b
ON CASE
WHEN a.code is not null and a.code = b.code THEN 1
WHEN a.type = b.type THEN 1
ELSE 0
END = 1
For your example you can reduce the code to just two joins, it can't be done in one as you are joining two different tables.
SELECT CASE WHEN RDIM.addres IS NULL THEN RDIM2.addres ELSE RDIM.addres
FROM rpt_dm_ee_intg.CASE_PERSON_ADDRESS dc
LEFT JOIN rpt_dm_ee_prsnt.REGION_DIM RDIM ON CASE
WHEN (dc.ZIP_CODE = RDIM.ZIP_CODE
AND RDIM.REGION_EFF_END_DT IS NULL
AND dc.addr_type_cd='PA'
AND dc.EFF_END_DT IS NULL) THEN 1
WHEN (dc.ZIP_CODE = RDIM1.ZIP_CODE
AND RDIM1.REGION_EFF_END_DT IS NULL
AND dc.addr_type_cd='MA'
AND DC.EFF_END_DT IS NULL) THEN 1
ELSE 0
END = 1
LEFT JOIN
(SELECT PARISH_CD,
min(REGION_DIM_SK) AS REGION_DIM_SK
FROM rpt_dm_ee_prsnt.REGION_DIM
WHERE REGION_EFF_END_DT IS NULL
GROUP BY PARISH_CD) RDIM2 ON CASE
WHEN (dc.addr_type_cd='PA'
AND dc.PARISH_CD = RDIM2.PARISH_CD
AND DC.EFF_END_DT IS NULL
AND RDIM.ZIP_CODE IS NULL) THEN 1
WHEN (dc.addr_type_cd='MA'
AND dc.PARISH_CD = RDIM3.PARISH_CD
AND DC.EFF_END_DT IS NULL
AND RDIM.ZIP_CODE IS NULL) THEN 1
ELSE 0
END = 1
edit
If you don't want to have nulls from RDIM2 table if RDIM1 zip code is present the logic could be easily extended to support that. You just need to add AND RDIM.ZIP_CODE IS NULL to CASE WHEN conditions.

SQL: If there are two rows that contain same record, want it to display one

based on my question above, below is the SQL
SELECT ets_tools.tools_id, ets_borrower.fullname, ets_team.team_name, ets_borrow.time_from,
ets_borrow.time_to, ets_borrow.borrow_id FROM ets_tools
INNER JOIN ets_tools_borrow ON ets_tools.tools_id = ets_tools_borrow.tools_id
INNER JOIN ets_borrow ON ets_borrow.borrow_id = ets_tools_borrow.borrow_id
INNER JOIN ets_borrower ON ets_borrower.badgeid = ets_borrow.badgeid
INNER JOIN ets_team ON ets_team.team_id = ets_borrower.team_id
WHERE ets_tools.borrow_id IS NOT NULL AND ets_borrow.status_id = 1 AND ets_borrow.time_to IS NULL
and the result display like this:
From the image above, we can see that the borrow_id with value 1 display two rows. Now, how to display only one borrow_id for value 1 since its duplicate the same things.
Anyone can help?
Assuming you want to retain the record having the smallest tools_id, you could aggregate by the other columns and take the MIN of tools_id:
SELECT
MIN(ets_tools.tools_id) AS tools_id,
ets_borrower.fullname,
ets_team.team_name,
ets_borrow.time_from,
ets_borrow.time_to,
ets_borrow.borrow_id
FROM ets_tools
INNER JOIN ets_tools_borrow ON ets_tools.tools_id = ets_tools_borrow.tools_id
INNER JOIN ets_borrow ON ets_borrow.borrow_id = ets_tools_borrow.borrow_id
INNER JOIN ets_borrower ON ets_borrower.badgeid = ets_borrow.badgeid
INNER JOIN ets_team ON ets_team.team_id = ets_borrower.team_id
WHERE
ets_tools.borrow_id IS NOT NULL AND
ets_borrow.status_id = 1 AND
ets_borrow.time_to IS NULL
GROUP BY
ets_borrower.fullname,
ets_team.team_name,
ets_borrow.time_from,
ets_borrow.time_to,
ets_borrow.borrow_id;
Try this:
Change the SELECT to SELECT TOP 1 WITH TIES
And at the end add ORDER BY ROW_NUMBER() OVER(PARTITION BY ets_borrow.borrow_id ORDER BY ets_tools.tools_id)

DB2 Update with subquery that contains join - correlating

I am trying to perform an update with a subquery where the update table is joined in the subquery. I cannot figure out how to associate the update record with the record found in the subquery. Something like the following, though as written this will obviously update the entire table. Thanks in advance.
Update OPTION OPT
SET (PSTREET, PCITY, PPROVINCE, PCOUNTRY, PPOSTALCODE)=
(select ADDRESS, CITY, PROVSTATE, COUNTRY, POSTALCODE
from ADDRESSES addr INNER JOIN COMPANY C ON C.SECURITYCOMPANY = addr.CODE1 || addr.CODE2
INNER JOIN DCODE D ON C.COMPANY_ID = D.COMPANY_ID
INNER JOIN OPTION OPT ON OPT.DCODE_ID = D.DCODE_ID
WHERE C.YEAROF IS NULL
AND C.DELETED IS NULL
AND D.DCODEEFF < CURRENT TIMESTAMP
AND (D.DCODEEXP IS NULL OR D.DCODEEXP > CURRENT TIMESTAMP)
AND D.DCODEELEMENT = addr.DCODEELEMENT
AND D.IND = addr.IND
AND ((addr.IND = 'B' AND addr.VAL1 = OPT.VAL1 AND addr.VAL2 = OPT.VAL2)
OR (addr.IND = 'Y' AND addr.VAL2 = OPT.VAL2)
OR (addr.IND = 'X' AND addr.VAL1 = OPT.VAL1))
You need to have some criteria to identify the records in OPT that you want to update. If its a single record, you'll need a surrogate or natural key. If its a many records like, say, based on a timestamp, you will need that time. Those criteria can then be put into a where clause after your set.
Use a condition in the WHERE clause in the sub-query that ties the result(s) in the sub-query to the outer table that you're updating. Here I added AND addr.addr_id = OPT.addr_id to your WHERE clause, but whatever the id column is called it needs to be shared between the table that you are updating and the sub-query.
UPDATE OPTION OPT
SET (PSTREET, PCITY, PPROVINCE, PCOUNTRY, PPOSTALCODE) =
(SELECT ADDRESS, CITY, PROVSTATE, COUNTRY, POSTALCODE
FROM ADDRESSES addr INNER JOIN COMPANY C ON C.SECURITYCOMPANY = addr.CODE1 || addr.CODE2
INNER JOIN DCODE D ON C.COMPANY_ID = D.COMPANY_ID
INNER JOIN OPTION OPT ON OPT.DCODE_ID = D.DCODE_ID
WHERE C.YEAROF IS NULL
AND C.DELETED IS NULL
AND D.DCODEEFF < CURRENT TIMESTAMP
AND (D.DCODEEXP IS NULL OR D.DCODEEXP > CURRENT TIMESTAMP)
AND D.DCODEELEMENT = addr.DCODEELEMENT
AND D.IND = addr.IND
AND ((addr.IND = 'B' AND addr.VAL1 = OPT.VAL1 AND addr.VAL2 = OPT.VAL2)
OR (addr.IND = 'Y' AND addr.VAL2 = OPT.VAL2)
OR (addr.IND = 'X' AND addr.VAL1 = OPT.VAL1)
AND addr.addr_id = OPT.addr_id)
In reality the criteria might be more complicated (involving a compound key or some inequality condition, for instance) - but regardless, adding the condition to the inner query is what's required.

SQL: Split rows with same ID into columns + left join

I have a cs cart database and I am trying to select all the attributes for all the products, the problem is that for each separate attribute for a product, my query creates a new row, I want to to have a single row for each products that has all the attributes into columns.
This is my query right now:
SELECT a.product_id, b.variant, c.description, d.product_code
FROM cscart_product_features_values a
LEFT JOIN cscart_product_feature_variant_descriptions b ON a.variant_id = b.variant_id
LEFT JOIN cscart_product_features_descriptions c ON a.feature_id = c.feature_id
LEFT JOIN cscart_products d ON a.product_id = d.product_id
After I run the query, I get the following result:
product_id;"variant";"description";"product_code"
38;"1st";"Grade Level";"750"
38;"Math";"Subject Area";"750"
38;"Evan-Moor";"Publisher";"750"
etc next product
What I want is this:
product_id;"product_code";"Grade Level";"Subject Area";"Publisher"
38;"750";"1st";"Math";"Evan-Moor"
etc next product
We only have 3 type of attributes: Grade Level, Subject Area and Publisher.
Any ideas how to improve my query and achieve this? I would be happy even with concatenating all 3 attributes in one column, delimited by ",".
This is a generic SQL solution using GROUP BY and MAX(case expression) to achieve the transformation of 3 rows into a single row with the 3 columns.
SELECT
v.product_id
, p.product_code
, MAX(CASE WHEN fd.description = 'Grade Level' THEN vd.variant END) AS GradeLevel
, MAX(CASE WHEN fd.description = 'Subject Area' THEN vd.variant END) AS SubjectArea
, MAX(CASE WHEN fd.description = 'Publisher' THEN vd.variant END) AS Publisher
FROM cscart_products p
LEFT JOIN cscart_product_features_values v ON p.product_id = v.product_id
LEFT JOIN cscart_product_feature_variant_descriptions vd ON v.variant_id = vd.variant_id
LEFT JOIN cscart_product_features_descriptions fd ON v.feature_id = fd.feature_id
GROUP BY
v.product_id
, p.product_code
This approach should work on just about any SQL database.
Note also that I have changed the order of tables because I presume there has to be a row in cscart_products, but there might not be related rows in the other tables.
I have also changed the aliases, personally I do not care for aliaes based on the order of use in a query (e.g. I just changed the order so I had to change all references). I have use 'p' = product, 'v' = variant, 'vd' = variant description & 'fd' = feature description' - with such a convention for aliases I can re-arrange the query without changing every reference.

Only return value that matches the ID on table 1

I have tried all possible joins and sub-queries but I cant get the data to only return one value from table 2 that exactly matches the vendor ID. If I dont have the address included in the query, I get one hit for the vendor ID. How can I make it so that when I add the address, I only want the one vendor that I get prior to adding the address.
The vendor from table one must be VEN-CLASS IS NOT NULL.
This was my last attempt using subquery:
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENMAST.VENDOR_CONTCT,
APVENMAST.TAX_ID,
Subquery.ADDR1
FROM (TEST.dbo.APVENMAST APVENMAST
INNER JOIN
(SELECT APVENADDR.ADDR1,
APVENADDR.VENDOR_GROUP,
APVENADDR.VENDOR,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENADDR APVENADDR
INNER JOIN TEST.dbo.APVENMAST APVENMAST
ON (APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP)
AND (APVENADDR.VENDOR = APVENMAST.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)) Subquery
ON (APVENMAST.VENDOR_GROUP = Subquery.VENDOR_GROUP)
AND (APVENMAST.VENDOR = Subquery.VENDOR))
INNER JOIN TEST.dbo.APVENLOC APVENLOC
ON (APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENLOC.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)
Try this:
SELECT APVENMAST.VENDOR_GROUP
, APVENMAST.VENDOR
, APVENMAST.VENDOR_VNAME
, APVENMAST.VENDOR_CONTCT
, APVENMAST.TAX_ID
, APVENADDR.ADDR1
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN (
select VENDOR_GROUP, VENDOR, ADDR1
, row_number() over (partition by VENDOR_GROUP, VENDOR order by ADDR1) r
from TEST.dbo.APVENADDR
) APVENADDR
ON APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP
AND APVENADDR.VENDOR = APVENMAST.VENDOR
AND APVENADDR.r = 1
--do you need this table; you're not using it...
--INNER JOIN TEST.dbo.APVENLOC APVENLOC
--ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
--AND APVENMAST.VENDOR = APVENLOC.VENDOR
WHERE APVENMAST.VEN_CLASS IS NOT NULL
--if the above inner join was to filter results, you can do this instead:
and exists (
select top 1 1
from TEST.dbo.APVENLOC APVENLOC
ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
AND APVENMAST.VENDOR = APVENLOC.VENDOR
)
I found another column in the APVENLOC table that I can filter on to get the unique vendor. Turns out if the vendor address is for the main office, the vendor location is set blank.
Easier than I thought it would be!
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENADDR.ADDR1,
APVENMAST.VENDOR_SNAME,
APVENADDR.LOCATION_CODE,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN TEST.dbo.APVENADDR APVENADDR
ON (APVENMAST.VENDOR_GROUP = APVENADDR.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENADDR.VENDOR)
WHERE (APVENADDR.LOCATION_CODE = ' ')
Shaji