Using max-function in subquery relating to main query - sql

I have the following problem:
I have two tables TAB_1 and TAB_2.
In TAB_1 I would like to fill in column FEAT_A1 with a matching value in TAB_2. Hence, I join both tables on their common feature FEAT_B.
Unfortunately, per FEAT_B2-ID in TAB_2 there are multiple records. I would like to use the matching record, for which FEAT_D2 is maximal.
I can perform this operation with the code shown below. Unfortunately, the code is super slow since, per match, I have to perform one order- and one fetch-operation.
Can you think of a more efficient way to program this?
To clarify things, here is an example of TAB_2 records:
FEAT_A2 FEAT_B2 FEAT_C2 FEAT_D2
"A" 42 "SOMETHING" 7
"B" 42 "SOMETHING" 11
"D" 42 "SOMETHING" 1
"A" 42 "SOMETHING" 3
In TAB_1, for FEAT_B1 = 42, I would like to set FEAT_A1 = "B".
UPDATE TAB_1
SET TAB_1.FEAT_A1 = (
SELECT FEAT_A2 FROM TAB_2
WHERE TAB1.FEAT_B1 = TAB2.FEAT_B2
AND TAB2.FEAT_C2 = 'SOMETHING'
ORDER BY TAB2.FEAT_D2 DESC
FETCH FIRST 1 ROW ONLY);

For this query:
UPDATE TAB_1
SET TAB_1.FEAT_A1 =
(SELECT FEAT_A2
FROM TAB_2
WHERE TAB1.FEAT_B1 = TAB2.FEAT_B2 AND
TAB2.FEAT_C2 = 'SOMETHING'
ORDER BY TAB2.FEAT_D2 DESC
FETCH FIRST 1 ROW ONLY
);
You want an index on tab_2(FEAT_B1, FEAT_C2, FEAT_D2 DESC). This should be a notable performance improvement.
That said, you are going to be updating all rows in TAB_1. If that table is no small, then there will be a lot of overhead just for doing the updates.

I would suggest using merge for it.
MERGE INTO TAB_1 T1
USING (SELECT FEAT_A2, FEAT_B2,
ROW_NUMBER() OVER (PARTITION BY FEAT_B2
ORDER BY FEAT_D2 DESC NULLS LAST) AS RN
FROM TAB_2) T2
ON (T1.FEAT_B1 = T2.FEAT_B2 AND T2.RN = 1)
WHEN MATCHED THEN
UPDATE SET T1.FEAT_A1 = T2.FEAT_A2
Cheers!!

Related

Remove duplicate reverse pairs

I am making a table of all product SKUs (fj1_sku column) paired with the product SKU of a second product which is a close substitute to that product (fj2_sku column) based on a range of criteria. To get the main matrix I have done a cross join of the final_join table with itself.
I cannot figure out how to remove the mirror duplicate pairs in the resulting table:
fj1_sku
fj2_sku
10
11
11
10
i.e. I only want one row with the pair 11 & 10.
I have tried a number of solutions found on this platform, but do not want to use a "select distinct" because I need many variables that I cannot use a distinct with.
Here is the code I have so far:
select
fj1.sku as fj1_sku,
fj2.sku as fj2_sku,
fj1.name as fj1_name,
fj2.name as fj2_name,
fj1.brand as fj1_brand,
fj2.brand as fj2_brand
from final_join as fj1
cross join final_join as fj2
where (fj1.sku <> fj2.sku) and (fj1.brand <> fj2.brand)
If you have duplicates for all pairs, then you can use:
select t.*
from t
where fj1_sku < fj2_sku;
If not, you can use be more selective:
select t.*
from t
where t.fj1_sku < t.fj2_sku or
not exists (select 1
from t t2
where t2.fj1_sku = t.fj2_sku and
t2.fj2_sku = t.fj1_sku
);
You can also incorporate similar logic into a delete, if you actually want to change the table.

Is hiding all rows of duplicated data possible?

I am building a report using a SQL Stored Parameter that I created. It pulls data that may have 1 line per unique number, or 2 lines per unique number. I want to only show rows where there is only 1 line.
I have tried changing visibility, but the closest I have come to is
=IIF(Fields!uniquenumber.Value = previous(Fields!uniquenumber.value,True,False)
but this hides 1 of the rows, and not both. I have also tried using CASE WHEN in the query to identify if there are more than 1 line, but I still haven't been able to hide when there is more than 1 line. My query is below (redacted quite a few lines of extra criteria that aren't relevant to my question here. There are quite a few instances where the first part of my WHERE statement will have data that also meets the criteria of the 2nd part.
SELECT
Reorders.LastRxNo,
Reorders_1.LastRxNo AS Reorders1LastRxNo,
Rxs_2.RxBatch AS Rxs2RxBatch,
Reorders.FacID,
KeyIdentifiers.GPI,
Reorders.LastFillDt,
Rxs_1.RxBatch as Rxs1RxBatch,
CASE
WHEN (Reorders.LastRxNo<>Reorders_1.LastRxNo) THEN 1
ELSE 0
END AS Duplicate
FROM Reorders
LEFT OUTER JOIN Rxs AS Rxs_1
ON Reorders.LastRXNo = Rxs_1.RxNo
RIGHT OUTER JOIN KeyIdentifiers
ON Reorders.NDC = KeyIdentifiers.NDC
INNER JOIN Patients
ON Reorders.FacID = Patients.FacID
AND Reorders.PatID = Patients.PatID
LEFT OUTER JOIN Reorders AS Reorders_1
ON Reorders.FacID = Reorders_1.FacID
AND Reorders.PatID = Reorders_1.PatID
AND KeyIdentifiers.NDC = Reorders_1.NDC
LEFT OUTER JOIN Rxs AS Rxs_2
ON Reorders_1.LastRxNo = Rxs_2.RxNo
WHERE
Reorders.ProfileOnly = 1
AND Rxs_1.RxBatch IS NULL
AND Reorders.CutOffDt IS NULL
AND Reorders.PackType LIKE 'PHDEF%'
AND Reorders.Auto = 1
AND Reorders.PhRxStatus IS NULL
AND Reorders.LastFillDt > '01/01/2019'
AND (Reorders.LastRxNo = Reorders_1.LastRxNo
OR Reorders.LastRxNo <> Reorders_1.LastRxNo AND Rxs_2.RxBatch IN ('CF','GONE'))
ORDER BY Reorders.FacID, Patients.PatLName, Patients.PatFName
Yields Results Similar to:
LastRxNo Reorders1LastRxNo Rxs2RxBatch Duplicate
111 111 null 0
222 222 null 0
222 444 CF 1
In the above example, I only need to see the lines like line 1. Since "LastRxNo" 222 has 2 lines, I do not want to see it on my final report. (However, it is queried, because when LastRxNo =222 AND Reorders1LastRxNo = 222 and Rxs2RxBatch IS NULL would pull if I didn't account for it in my WHERE statement, I think. I am happy doing this any way possible, whether it be in the query or in SSRS report.
Replace your CASE ... as duplicate with
COUNT(*) OVER(PARTITION BY LastRxNo) AS LastRxNoCount
then
wrap the whole query in another select like this...
SELECT * FROM
(
your original query here including the change stated above
) q
WHERE q.LastRxNoCount =1
Just filter out all rows where the number of rows is greater than 1:
SELECT LastRxNo, RxBatch
FROM Reorders
WHERE RxBatch IN ('CF','GONE')
AND Reorders.LastRxNo IN
(SELECT LastRxNo FROM Reorders GROUP BY LastRxNo HAVING COUNT(LastRxNo) = 1)
You should just be able to add the last two lines into your WHERE clause.
Alternatively, you could add it to your JOIN conditions:
SELECT
Reorders.LastRxNo
FROM Reorders
INNER JOIN (
SELECT LastRxNo FROM Reorders
GROUP BY LastRxNo
HAVING COUNT(LastRxNo) = 1
) AS UniqueRxNo ON UniqueRxNo.LastRxNo = Reorders.LastRxNo
Yes, you can do it one of 2 ways:
is in your data source, do a distinct select.
Set the "Hide Duplicates" property on the detail line in the report properties

Oracle SQL update statement how to update two rows at a time

I tried to update two rows in a table.
If I used:
update ERNTESTUPDATE a
set (date_loaded, acad_career) = (select distinct b.date_loaded, b.acad_career
from PS_STDNT_ENRL b
where rownum=1)
where to_char(date_created) = to_char(trunc(sysdate))
;
I got 2 rows updated as
11/29/18 UGRD
11/29/18 UGRD
If I used:
update ERNTESTUPDATE a
set (date_loaded, acad_career) = (select distinct b.date_loaded, b.acad_career
from PS_STDNT_ENRL b
where a.date_loaded = b.date_loaded )
where to_char(date_created) = to_char(trunc(sysdate))
;
I got an error
single-row subquery returns more than one row
How to get a result as below:
11/29/18 UGRD
11/29/18 GRAD
Thank you,
Kate
The subquery needs to return a SINGLE row. That works well when you include the condition rownum=1 in the first one:
select distinct b.date_loaded, b.acad_career
from PS_STDNT_ENRL b
where rownum=1 -- this filter makes sure only one row is returned
If you remove that condition, then the subquery will return multiple rows, and that makes the whole UPDATE fail.

SQL Statement to select row where previous row status = 'C' AS400

This is being run on sql for IBMI Series 7
I have a table which stores info about orders. Each row has an order number (ON), part number(PN), and sequence number(SEQ). Each ON will have multiple PN's linked to them and each part number has multiple SEQ Number. Each sequence number represents the order in which to do work on the part. Somewhere else in the system once the part is at a location and ready to be worked on it shows a flag. What I want to do is get a list of orders for a location that have not yet arrived but have been closed out on the previous location( Which means the part is on it's way).
I have a query listed below that I believe should work but I get the following error: "The column qualifier or table t undefined". Where is my issue at?
Select * From (SELECT M2ON as Order__Number , M2SEQ as Sequence__Number,
M2PN as Product__Number,ML2OQ as Order__Quantity
FROM M2P
WHERE M2pN in (select R1PN FROM R1P WHERE (RTWC = '7411') AND (R1SEQ = M2SEQ)
)
AND M2ON IN (SELECT M1ON FROM M1P WHERE ML1RCF = '')
ORDER BY ML2OSM ASC) as T
WHERE
T.Order__Number in (Select t3.m2on from (SELECT *
FROM(Select * from m2p
where m2on = t.Order__Number and m2pn = t.Product__Number
order by m2seq asc fetch first 2 rows only
)as t1 order by m2seq asc fetch first row only
) as t3 where t3.m2stat = 'C')
EDIT- Answer for anyone else with this issue
Clutton's Answer worked with slight modification so thank you to him for the fast response! I had to name my outer table and specify that in the subquery otherwise the as400 would kick back and tell me it couldn't find the columns. I also had to order by the sequence number descending so that I grabbed the highest record that was below the parameter(otherwise for example if my sequence number was 20 it could grab 5 even though 10 was available and should be shown first. Here is the subquery I now use. Please note the actual query names m2p as T1.
IFNULL((
SELECT
M2STAT
FROM
M2P as M2P_1
WHERE
M2ON = T1.M2ON
AND M2SEQ < T1.M2SEQ
AND M2PN IN (select R1PN FROM R1P WHERE (RTWC = #WC) AND (R1SEQ = T1.M2SEQ))
ORDER BY M2SEQ DESC
FETCH FIRST ROW ONLY
), 'NULL') as PRIOR_M2STAT
Just reading your question, it looks like something I do frequently to emulate RPG READPE op codes. Is the key to M2P Order/Seq? If so, here is a basic piece that may help you build out the rest of the query.
I am assuming that you are trying to get the prior record by key using SQL. In RPG this would be like doing a READPE on the key for a file with Order/Seq key.
Here is an example using a subquery to get the status field of the prior record.
SELECT
M2ON, M2PN, M2OQ, M2STAT,
IFNULL((
SELECT
M2STAT
FROM
M2P as M2P_1
WHERE
M2P_1.M2ON = M2ON
AND M2P_1.M2SEQ < M2SEQ
FETCH FIRST ROW ONLY
), '') as PRIOR_M2STAT
FROM
M2P
Note that this wraps the subquery in an IFNULL to handle the case where it is the first sequence number and no prior sequence exists.

Different results on the basis of different foreign key value using a falg in where clause

Please see attached image.
alt text http://img241.imageshack.us/img241/3585/customcost.png
Can you please tell me what query will work. Please ignore isdefinite and productpriceid columns.
Thanks
If you want a single query, this should do it if I've interpreted your question properly:
SELECT DISTINCT t1.SupplierVenueProductID, [...]
FROM table t1
LEFT JOIN
table t2
ON t1.SupplierVenueProductID = t2.SupplierVenueProductID
AND t2.iscustomcost = 1
WHERE t2.SupplierVenueProductID IS NULL
OR t1.iscustomcost = 1
I don't know your table name, but you join it to itself.
I'm a bit lost on what you want to accomplish here,
going by your requirement if isCustomCost = 1 then return record #3 from SupplierVenueProductId 1 and both records from SupplierVenueProductId 2
Trying to generalize this, I think what you need is :
return all rows from the table, unless when there is a record for a SupplierVenueProductId that has isCustomCost = 1, then only return that record for this SupplierVenueProductId
Which then becomes something along the lines of :
SELECT t1.*
FROM myTable t1
WHERE t1.isCustomCost = 1
OR NOT EXISTs (SELECT *
FROM t2
WHERE t2.SupplierVenueProductId = t1.SupplierVenueProductId
AND t2.isCustomCost = 1)
Hope this helps.