Differential Data Merge is complex? How to get results? - sql

I am looking at an old stored procedure that's job is to preserve the New sort order based on yesterday's and today's data.
Sort orders are not being preserved any longer and I have narrowed it down to the WHERE clause eliminating all rows. The main goal is to preserve the SortOrder so if some custom data was in position 4 yesterday, any NEW custom data that takes its place should ALSO have position 4.
If I eliminate
--AND b.PrimaryID = b.SortOrder
then I get thousands of rows. I suspect something is wrong but it I am not understanding. How can I make this simpler so it is REALLY easy to understand?
IMPORTANT: the SortOrder actually equals the PrimaryID if the data is no longer sorted. Otherwise it is incremental 1 2 3 4 5 6 7 .. and so on. I guess this was the original architects way of doing it.
-- Merge data and get missing rows that have not changed.
SELECT
PrevPrimaryID = a.PrimaryID
,a.WidgetID
,a.AnotherValue
,a.DataID
,PrevSortOrder = a.SortOrder
,NewPrimaryID = b.PrimaryID
,NewDataID = b.DataID
,NewStartDate = b.StartDate
,NewSortOrder = b.SortOrder
INTO #NewOrder2
FROM #YesterdaysData2 a
LEFT JOIN #TodaysData2 b ON a.WidgetID = b.WidgetID
AND a.AnotherValue = b.AnotherValue
WHERE
a.Primaryid <> a.sortorder
AND b.PrimaryID = b.SortOrder
SELECT * FROM #NewOrder2
-- later update based on #NewOrder2...
UPDATE CustomerData
SET SortOrder = (
SELECT PrevSortOrder
FROM #NewOrder2
WHERE CustomerData.PrimaryID = #NewOrder2.NewPrimaryID
)
WHERE PrimaryID IN (
SELECT NewPrimaryID
FROM #NewOrder2
)
UPDATE - Is it possible its just a blunder and the WHERE clause should be
WHERE a.Primaryid <> a.sortorder
AND b.PrimaryID <> b.SortOrder

Related

IF / Case statment in SQL

I have a column where I have 0 or 1. I like to do the following set up:
If 0 than put / use the Region_table (here I have regions like EMEA, AP,LA with finished goods only) and when it 1 then put / use the Plant_table (here I have plants with non-finished goods) data's.
I tried to write it in 2 different statements but it is not good:
,Case
when [FG_NFG_Selektion] = '0' Then 'AC_region'
End as 'AC_region'
,Case
when [FG_NFG_Selektion] = '1' Then 'AC_plant'
End as 'AC_plant'
I'm not 100% clear on what you're looking for, but if you want to get data from different tables based on the value in the [FG_NFG_Selektion] field, you can do something like this:
SELECT
CASE
WHEN [FG_NFG_Selektion] = '0' THEN r.some_col -- If 0, use value from "region" table
WHEN [FG_NFG_Selektion] = '1' THEN p.some_col -- If 1, use value from "plant" table
END AS new_field
FROM MyTable t
LEFT JOIN AC_region r ON t.pk_col = r.pk_col -- get data from "AC_region" table
LEFT JOIN AC_plant p ON t.pk_col = p.pk_col -- get data from "AC_plant" table
;
If [FG_NFG_Selektion] is a numeric field, then you should remove the single quotes: [FG_NFG_Selektion] = 0.
I would strongly recommend putting the conditions in the ON clauses:
SELECT COALESCE(r.some_col, p.some_col) as som_col
FROM t LEFT JOIN
AC_region r
ON t.pk_col = r.pk_col AND
t.FG_NFG_Selektion = '0' LEFT JOIN
AC_plant p
ON t.pk_col = p.pk_col AND
t.FG_NFG_Selektion = '1';
Why do I recommend this? First, this works correctly if there are multiple matches in either table. That is probably not an issue in this case, but it could be in others. You don't want to figure out where extra rows come from.
Second, putting the conditions in the ON clause allows the optimizer/execution engine to take advantage of them. For instance, it is more likely to use FG_NFG_Selektion in an index.

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

Joining WIP_DISCRETE_JOBS to a Bill Of Materials table

I have an SQL query in which I am trying to fetch items along with their components which have Bill Of Materials (BOM) enabled and whose creation date is withing the last three years.
The problem is every item along with their component list gets repeated. Suppose an item has 5 components, and I query with the particular item number. I get 35 rows (7 repetitions of sets of 5 components). However, if I query with a particular WIP_ENTITY_NAME or if I use DISTINCT, I get a perfect 5 rows of data. I need to get this data (5 rows, no repetitions) when I query with a particular item number.
Here is the query:
SELECT *
FROM BOM_BILL_OF_MATERIALS BOM,
BOM_INVENTORY_COMPONENTS BIC,
BOM_COMPONENTS_B BCB,
BOM_OPERATIONAL_ROUTINGS_V BORV,
BOM_OPERATION_SEQUENCES_V BOSV,
BOM_STRUCTURES_B BSB,
BOM_STRUCTURE_TYPES_B BST,
MTL_SYSTEM_ITEMS_B MSI,
MTL_SYSTEM_ITEMS_TL MSIT,
ORG_ORGANIZATION_DEFINITIONS OOD,
WIP_DISCRETE_JOBS_V WDJV
WHERE BOM.BILL_SEQUENCE_ID = BIC.BILL_SEQUENCE_ID
AND BIC.COMPONENT_SEQUENCE_ID = BCB.COMPONENT_SEQUENCE_ID
AND BCB.BILL_SEQUENCE_ID = BSB.BILL_SEQUENCE_ID
AND BOM.STRUCTURE_TYPE_ID = BST.STRUCTURE_TYPE_ID
AND BOM.ASSEMBLY_ITEM_ID = MSI.INVENTORY_ITEM_ID
AND BOM.ORGANIZATION_ID = MSI.ORGANIZATION_ID
AND BOSV.ROUTING_SEQUENCE_ID = BORV.ROUTING_SEQUENCE_ID
AND BORV.ASSEMBLY_ITEM_ID = BOM.ASSEMBLY_ITEM_ID
AND BORV.ORGANIZATION_ID = BOM.ORGANIZATION_ID
AND MSI.INVENTORY_ITEM_ID = MSIT.INVENTORY_ITEM_ID
AND MSI.ORGANIZATION_ID = MSIT.ORGANIZATION_ID
AND MSIT.ORGANIZATION_ID = OOD.ORGANIZATION_ID
AND MSIT.LANGUAGE = USERENV('LANG')
AND sysdate BETWEEN BCB.EFFECTIVITY_DATE AND NVL(BCB.DISABLE_DATE, sysdate)
AND MSI.BOM_ENABLED_FLAG = 'Y'
AND NVL(MSI.ENABLED_FLAG,'X') ='Y'
AND OOD.ORGANIZATION_ID IN (203,204,328)
AND BORV.COMMON_ROUTING_SEQUENCE_ID = WDJV.COMMON_ROUTING_SEQUENCE_ID
AND BORV.ASSEMBLY_ITEM_ID = WDJV.PRIMARY_ITEM_ID
AND WDJV.CREATION_DATE > ADD_MONTHS( sysdate, -12*3 )
AND WDJV.WIP_ENTITY_NAME = '28799'
--and MSI.SEGMENT1='9064090'
Also, I have found that the sets of 5 differ from each other on the basis of their WE_ROW_ID, REQUEST_ID_7, SCHEDULED_START_DATE, SCHEDULED_COMPLETION_DATE and other columns which are present in the WIP_DISCREET_JOBS table. Is there any way to map these columns to any of the BOM tables? I am new to this, so please bear with me gurus.
I see you are asking about the Oracle e-Business Suite data model.
trying to fetch items ... and whose creation date is withing the last three years.
That is not what your query is doing. You are getting details of items made (via a WIP discrete job) within the last 3 years -- and you are getting a duplicate of the item for every time it was made (i.e., every occurrence in WIP_DISCRETE_JOBS_V).
If you want items made in the last 3 years, but you only want each item once, you should use an EXISTS (semi-join) to filter the rows instead of actually joining to WIP_DISCRETE_JOBS_V, which will cause duplicates). Something more along these lines:
SELECT *
FROM bom_bill_of_materials bom,
bom_inventory_components bic,
bom_components_b bcb,
bom_operational_routings_v borv,
bom_operation_sequences_v bosv,
bom_structures_b bsb,
bom_structure_types_b bst,
mtl_system_items_b msi,
mtl_system_items_tl msit,
org_organization_definitions ood --,
--wip_discrete_jobs_v wdjv
WHERE bom.bill_sequence_id = bic.bill_sequence_id
AND bic.component_sequence_id = bcb.component_sequence_id
AND bcb.bill_sequence_id = bsb.bill_sequence_id
AND bom.structure_type_id = bst.structure_type_id
AND bom.assembly_item_id = msi.inventory_item_id
AND bom.organization_id = msi.organization_id
AND bosv.routing_sequence_id = borv.routing_sequence_id
AND borv.assembly_item_id = bom.assembly_item_id
AND borv.organization_id = bom.organization_id
AND msi.inventory_item_id = msit.inventory_item_id
AND msi.organization_id = msit.organization_id
AND msit.organization_id = ood.organization_id
AND msit.language = USERENV ('LANG')
AND SYSDATE BETWEEN bcb.effectivity_date AND NVL (bcb.disable_date, SYSDATE)
AND msi.bom_enabled_flag = 'Y'
AND NVL (msi.enabled_flag, 'X') = 'Y'
AND ood.organization_id IN (203, 204, 328)
--AND borv.common_routing_sequence_id = wdjv.common_routing_sequence_id
--AND borv.assembly_item_id = wdjv.primary_item_id
--AND wdjv.creation_date > ADD_MONTHS (SYSDATE, -12 * 3)
--AND wdjv.wip_entity_name = '28799'
AND EXISTS ( SELECT 'discrete job within the last 3 years'
FROM wip_discrete_jobs_v wdjv
WHERE wdjv.common_routing_sequence_id = borv.common_routing_sequence_id
AND wdjv.primary_item_id = borv.assembly_item_id
AND wdjv.creation_date >= ADD_MONTHS(SYSDATE, -12*3)
)
--and MSI.SEGMENT1='9064090'
Note -- I kept this as close to your original query as possible and I didn't verify all the join conditions you used. So there may be some other bugs in your initial query that I have inadvertently duplicated.
One other thing, it's not a good practice to use the forms views, like WIP_DISCRETE_JOBS_V or BOM_OPERATIONAL_ROUTINGS_V. These views are to provide data to online forms. Not only can your performance suffer (because they contain joins that you may not need), but, if you go to Oracle's eTRM site (http://etrm.oracle.com/pls/etrm/etrm_pnav.show_object?c_name=BOM_OPERATIONAL_ROUTINGS_V&c_owner=APPS&c_type=VIEW), you will see this warning:
Warning: Oracle does not recommend you query or alter datausing this
view. It may change dramatically in subsequent minor or major
releases.
It is better to SELECT from the base tables.

Set One Column Equal to Another In Different Tables

I am fairly new to SQL, and I am trying to create an SQL command checks if something is equal to something in one table, then update the value of something in another table. I have looked for solutions online, but I am not quite good enough to determine if what I have seen can be used to accomplish what I want to accomplish.
So here are the actual names of the tables and columns I am working with:
Item (Uses "ID")
SaleType (int, not null)
nitroasl_pamtable (Uses "ItemID")
PAM_SpecialOffer (bit, not null)
The "ID" that is shared between products in both tables are as follows:
- The "ID" column in the "Item" table
- The "ItemID" column in the "nitroasl_pamtable" table
What I need to do is go through the "Item" table and find all products that have "SaleType" equals "1"- Then update those IDs in "nitroasl_pamtable" by setting "PAM_SpecialOffer" equal to "1".
Is the following able to do what I want (this is a very rough guess btw)?
UPDATE nitroasl_pamtable
SET PAM_SpecialOffer = SaleType
FROM Item
INNER JOIN nitroasl_pamtable
ON ID = ItemID
WHERE SaleType = 1
I hope that the above makes sense, as I have found it a little hard to put into words, but in a nutshell, I am trying to mark all products with "SaleType=1" with "PAM_SpecialOffer=1" across the two different tables using "ID" and "ItemID" (respectively).
UPDATE
So, the following got me a listing of the set that I want to change. The IDs match up perfectly, etc. Now how can I UPDATE the PAM_SpecialOffer column with "1" (how do I change the following code to do this)?
SELECT i.ID, i.SaleType, i.SaleStartDate, i.SaleEndDate, i.ItemLookupCode, n.ItemID, n.PAM_SpecialOffer
FROM Item AS i
JOIN nitroasl_pamtable AS n
ON i.ID = n.ItemID
WHERE i.SaleType = 1
and (i.SaleStartDate > '2015-01-01' or i.SaleStartDate = '1899-12-31')
and i.SaleEndDate > getdate();
It would appear that your statement would work. However, I would write it as:
UPDATE pt
SET PAM_SpecialOffer = i.SaleType
FROM nitroasl_pamtable pt INNER JOIN
Item i
ON i.ID = pt.ItemID
WHERE i.SaleType = 1;
The i and pt are table aliases, abbreviations for the table name. In some cases, they are necessary. Here, they just clarify what the query is doing and where the columns are coming from.
As a rule, when I do an update with join, I put the table being updated first in the list of joins.
Here is what I ended up with. I do need to mark all products that do not receive the PAM_SpecialOffer = 1 update with PAM_SpecialOffer = NULL, but that should be easy enough!
UPDATE n
SET n.PAM_SpecialOffer = 1
FROM Item AS i
JOIN nitroasl_pamtable AS n
ON i.ID = n.ItemID
WHERE i.SaleType = 1
and (i.SaleStartDate > '2015-01-01' or i.SaleStartDate = '1899-12-31')
and i.SaleEndDate > getdate();
UPDATE - Final Revision
So I made some additional modifications to my query. I am sure there is a better way to write this, but this is the best I could do as far as cleaning up the non-valid 'PAM_SpecialOffer = 1' rows. Tested with a backup of our DB- works great! I'm gonna set this to run every few minutes on our DB.
/* Mark appropriate Sale items as 'PAM_SpecialOffer = 1' */
UPDATE n
SET n.PAM_SpecialOffer = 1
FROM Item AS i
JOIN nitroasl_pamtable AS n
ON i.ID = n.ItemID
WHERE i.SaleType >= 1
and (i.SaleStartDate >= '2015-04-01' or i.SaleStartDate = '1899-12-31')
and i.SaleEndDate >= getdate();
/* Cleanup 'PAM_SpecialOffer' */
UPDATE n
SET n.PAM_SpecialOffer = NULL
FROM Item AS i
JOIN nitroasl_pamtable AS n
ON i.ID = n.ItemID
WHERE i.SaleType < 1
or (i.SaleStartDate < '2015-04-01' and i.SaleStartDate <> '1899-12-31')
or i.SaleEndDate < getdate();

Update 1 field in a table from another field in a different table (OS400, not a 1 to 1 relationship)

Im trying to update a field in a table from another field in a different table.
The table being updated will have multiple records that need updating from 1 match in the other table.
Example, i have a 1 million row sales history file. Those million records have aproximately 40,000 different sku codes, each row has a date and time stamp. Each sku will have multiple records in there.
I added a new field called MATCOST (material cost).
I have a second table containing SKU and the MATCOST.
So i want to stamp every line in table 1 with the corresponding SKU's MATCOST in table2. I cannot seem to achieve this when its not a 1 to 1 relationship.
This is what i have tried:
update
aulsprx3/cogtest2
set
matcost = (select Matcost from queryfiles/coskitscog where
aulsprx3/cogtest2.item99 = queryfiles/coskitscog.ITEM )
where
aulsprx3/cogtest2.item99=queryfiles/coskitscog.ITEM
But that results in the SQL error: Column qualifier or table COSKITSCOG undefined and highlighting the q in the last reference to queryfiles/coskitscog.Item
Any idea's ?
Kindest Regards
Adam
Update: This is what my tables look like in principle. 1 Table contains the sales data, the other contains the MATCOSTS for the items that were sold. I need to update the Sales Data table (COGTEST2) with the data from the COSKITCOG table. I cannot use a coalesce statement because its not a 1 to 1 relationship, most select functions i use result in the error of multiple selects. The only matching field is Item=Item99
I cant find a way of matching multiple's. In the example we would have to use 3 SQL statements and just specify the item code. But in live i have about 40,000 item codes and over a million sales data records to update. If SQL wont do it, i suppose i'd have to try write it in an RPG program but thats way beyond me for the moment.
Thanks for any help you can provide.
Ok this is the final SQL statement that worked. (there were actually 3 values to update)
UPDATE atst2f2/SAP20 ct
SET VAL520 = (SELECT cs.MATCOST
FROM queryfiles/coskitscog cs
WHERE cs.ITEM = ct.pnum20),
VAL620 = (SELECT cs.LABCOST
FROM queryfiles/coskitscog cs
WHERE cs.ITEM = ct.pnum20),
VAL720 = (SELECT cs.OVRCOST
FROM queryfiles/coskitscog cs
WHERE cs.ITEM = ct.pnum20),
WHERE ct.pnum20 IN (SELECT cs.ITEM
FROM queryfiles/coskitscog cs)
This more compact way to do the same thing should be more efficient, eh?
UPDATE atst2f2/SAP20 ct
SET (VAL520, VAL620, VAL720) =
(SELECT cs.MATCOST, cs.LABCOST, cs.OVRCOST
FROM queryfiles/coskitscog cs
WHERE cs.ITEM = ct.pnum20)
WHERE ct.pnum20 IN (SELECT cs.ITEM
FROM queryfiles/coskitscog cs)
Qualify the columns with correlation names.
UPDATE AULSPRX3/COGTEST2 A
SET A.matcost = (SELECT matcost
FROM QUERYFILES/COSKITSCOG B
WHERE A.item99 = B.item)
WHERE EXISTS(SELECT *
FROM QUERYFILES/COSKITSCOG C
WHERE A.item99 = C.item)
From UPDATE, I'd suggest:
update
aulsprx3/cogtest2
set
(matcost) = (select Matcost from queryfiles/coskitscog where
aulsprx3/cogtest2.item99 = queryfiles/coskitscog.ITEM)
where
aulsprx3/cogtest2.item99=queryfiles/coskitscog.ITEM
Note the braces around matcost.