DB2 Update with subquery that contains join - correlating - sql

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.

Related

SQL Server - How to do Inner Join when condition could be empty?

I am trying to get the Country from my factories AddressID directly in one Query with an Inner join. My problem is that the AddressID could be an empty string.
How do i check that and do some kind of exception or something similar? I if address ID is empty i need an empty string as result for f.Country.
This is my query for now, but if AddressID = '' then the row is not showing up in my result.
SELECT o.FactoryID,o.Name,o.Rating,o.ProductCategory,o.Emissions,o.LatestChanges,o.AddressID,o.ProductTags,f.Country
FROM FactoryHistory o
INNER JOIN AddressHistory f on f.AddressID = o.AddressID
WHERE NOT o.LatestChanges = 'Deleted' AND o.IsCurrent = 1 AND f.IsCurrent = 1
I something in my question is missing or unclear, just let me know!
Just left join:
SELECT o.FactoryID, o.Name, o.Rating, o.ProductCategory, o.Emissions, o.LatestChanges, o.AddressID, o.ProductTags, f.Country
FROM FactoryHistory o
LEFT JOIN AddressHistory f on f.AddressID = o.AddressID AND f.IsCurrent = 1
WHERE NOT o.LatestChanges = 'Deleted' AND o.IsCurrent = 1
If you still want to filter out factories whose AddressID is not null (assuming that by empty you mean null) and that do not exist in the address table, than you can add a condition in the WHERE clause:
WHERE
NOT o.LatestChanges = 'Deleted'
AND o.IsCurrent = 1
AND (o.AddressID IS NULL OR f.AddressID IS NOT NULL)
It might be clearer with a negation:
WHERE
NOT o.LatestChanges = 'Deleted'
AND o.IsCurrent = 1
AND NOT (o.AddressID IS NOT NULL AND f.AddressID IS NULL)

How to Get a Count of Records Using Partitioning in Oracle

I have the following query:
SELECT
F.IID,
F.E_NUM AS M_E_NUM,
MCI.E_NUM AS MCI_E_NUM,
F.C_NUM AS M_C_NUM,
MCI.C_NUM AS MCI_C_NUM,
F.ET_ID AS M_ET_ID,
EDIE.ET_ID AS ED_INDV_ET_ID,
COUNT(*) OVER (PARTITION BY F.IID) IID_COUNT
FROM FT_T F JOIN CEMEI_T MCI ON F.IID = MCI.IID
JOIN EDE_T EDE ON MCI.E_NUM = EDE.E_NUM
JOIN EDIE_T EDIE ON EDIE.IID = F.IID AND EDIE.ET_ID = EDE.ET_ID
WHERE
F.DEL_F = 'N'
AND MCI.EFF_END_DT IS NULL
AND MCI.TOS = 'BVVB'
AND EDE.PTEND_DT IS NULL
AND EDE.DEL_S = 'N'
AND EDE.CUR_IND = 'A'
AND EDIE.TAR_N = 'Y'
AND F.IID IN
(
SELECT DISTINCT IID
FROM FT_T
WHERE GROUP_ID = 'BG'
AND DEL_F = 'N'
AND (IID, E_NUM) NOT IN
(
SELECT IID, E_NUM FROM CEMEI_T
WHERE TOS = 'BVVB' AND EFF_END_DT IS NULL
)
);
I am basically grabbing information from several tables and creating a flat record of them.
Everything works accordingly except now I need to find out whether there are two records in FT_T table with identical IID's and display that count as part of the result set.
I tried to use partitioning but all the rows in the result set return a single count even though there are ones that have 2 records with identical IID's in FT_T.
The reason I initially said that I'm gathering information from several tables is due to the fact that FT_T might not have all the information I need if two records are not available for the same IID, so I have to retrieve them from other tables JOINed in the query. However, I need to know which FT_T.IID's have two records in FT_T (or greater than one).
Perhaps you need to calculate the count before the join and filtering:
SELECT . . .
FROM (SELECT F.*,
COUNT(*) OVER (PARTITION BY F.IID) as IID_CNT
FROM FT_T F
) JOIN
CEMEI_T MCI
ON F.IID = MCI.IID JOIN
EDE_T EDE
ON MCI.E_NUM = EDE.E_NUM JOIN
EDIE_T EDIE
ON EDIE.IID = F.IID AND EDIE.ET_ID = EDE.ET_ID
. . .
this is merely a comment/observation, but formatting is needed
You use of in(...) with select distinct and not in(...,...) seems complex and could be a problem if some values are NULL. I suggest you consider using EXISTS and NOT EXISTS instead. e.g.
AND EXISTS (
SELECT
NULL
FROM FT_T
WHERE F.IID = FT_T.IID
AND FT_T.GROUP_ID = 'BG'
AND FT_T.DEL_F = 'N'
AND NOT EXISTS (
SELECT
NULL
FROM CEMEI_T
WHERE FT_T.IID = CEMEI_T.IID
AND FT_T.E_NUM = CEMEI_T.E_NUM
AND CEMEI_T.TOS = 'BVVB'
AND CEMEI_T.EFF_END_DT IS NULL
)
)

update with subquery

The code :
UPDATE tt_t_documents
SET t_Doc_header_ID = (SELECT
MIN(dh.Doc_header_ID)
FROM tt_t_documents td WITH (NOLOCK)
JOIN Doc_header dh WITH (NOLOCK)
ON dh.DH_doc_number = td.t_dh_doc_number
AND dh.DH_sub = 1
JOIN Pred_entry pe WITH (NOLOCK)
ON pe.Pred_entry_ID = dh.DH_pred_entry
JOIN Doc_type dt WITH (NOLOCK)
ON dty.Doc_type_ID = pe.PD_doc_type
AND dt.DT_mode = 5
HAVING COUNT(dh.Doc_header_ID) = 1);
I want to update my columns, but before that I also want to check if there is only one ID found.
The problem in this select is that I get more than one ID.
How can I write a query that updates each row and checks in the same query that there is only one id found?
I am guessing that you intend something like this:
update td
set t_Doc_header_ID = min_Doc_header_ID
from tt_t_documents td join
(select DH_doc_number, min(dh.Doc_header_ID) as min_Doc_header_ID
from Doc_header dh join
Pred_entry pe
on pe.Pred_entry_ID = dh.DH_pred_entry join
Doc_type dt
on dty.Doc_type_ID = pe.PD_doc_type and dt.DT_mode = 5
where dh.DH_doc_number = td.t_dh_doc_number and dh.DH_sub = 1
group by DH_doc_number
having count(dh.Doc_header_ID) = 1
) dh
on dh.DH_doc_number = td.t_dh_doc_number;
Using a join also means that you do not update the values where the condition does not match. If you use a left join, then the values will be updated to NULL (if that is your intention).
I'm not sure I believe you are getting more than one id back with that select given that you are doing a 'min' on it and have no group by. It should be only returning the lowest value for Doc_header_id.
To do what you are asking, first, you should have some way of joining to the tt_t_documents table in the update statement (eg. where td.id == tt_t_documents.id).
Second, you could re-write it to use the sub-query in the from. Something like:
update
tt_t_documents
set
t_Doc_header_ID = x.Doc_Header_id
from tt_t_documents join (
select td.id,
min(dh.Doc_header_ID)
from
tt_t_documents td
join Doc_header dh
on dh.DH_doc_number = td.t_dh_doc_number
and dh.DH_sub = 1
join Pred_entry pe
on pe.Pred_entry_ID = dh.DH_pred_entry
join Doc_type dt
on dty.Doc_type_ID = pe.PD_doc_type
and dt.DT_mode = 5
group by td.id
having
count(dh.Doc_header_ID) = 1
) x on tt_t_documents.id= x.id;
The syntax may not be perfect and I'm not sure how you want to find the doc_header_id but it would be something like this. The sub query would only return values with 1 doc_header_id). Not knowing the schema of your tables, this is as close as I can get.

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

updating Table from Another Table with Criteria

i am trying to write a query that will update the schemeid of a row with the id of the matching row from another table.
tblschemes b holds the schemes detail and tblclaims_liberty a holds the claims data and the fields to join on are b.nett_scheme = a.schemename and a.agentcode = b.agentcode
why then id the query below allocating a schemeid to rows of data that do not match the agentcode??
update tblclaims_liberty
set tblclaims_liberty.schemeid = tblschemes.id
from tblschemes inner join tblclaims_liberty on tblclaims_liberty.schemename = tblschemes.nett_scheme and tblclaims_liberty.agentcode = tblschemes.agentcode
where
ce_report = 'yes'
and (tblclaims_liberty.schemeid != tblschemes.id or schemeid is null) --only updates rows that require updateing instead of 6mil+ lines of data.
can someone point me in the right direction?? why is it not recognising the agentcode match?
regards,
Adam
Try with this:
update tblclaims_liberty TLtoUpdate
set TLtoUpdate.schemeid =
(
select TS.id
from tblschemes TS inner join tblclaims_liberty TL on TL.schemename = TS.nett_scheme and TL.agentcode = TS.agentcode
where TL.schemeid = TLtoUpdate.schemeid
)
where TLtoUpdate.ce_report = 'yes' and TLtoUpdate.schemeid is null
Looks there was missing the TL.id condition in the subquery.