How To Keep Records in Order from Derived Table - sql

I am trying to update a SQL table from a remote DB2 table. There may be multiple updates for the same record but I need the updates to happen in the order they are in in the DB2 table. You can not use Order By on a derived table. I have tried several different options to try to get this to work, but the updates still do not happen in order.
For example:
Change 1 - CUSTOMER NAME = ABCX
Change 2 - CUSTOMER NAME = ABC
After I run the query, the customer name is ABCX when it should be ABC.
I truly do not know what else to try. I've tried temp tables (still derived table), creating a concatenated field with date and time fields, sub-select, row_number() over(order by date, time) and many other things. I'd like to keep it in order by the date and time fields in the remote table.
Any insight would be appreciated.
Thank you.
Here is the basic code I have:
SET
A.CUSRID = B.RECORD_ID,
A.CUSSTS = B.ACTIVE_CODE,
A.CUSCOM = B.COMPANY_NUMBER,
A.CUSMNM = B.CUSTOMER_NAME,
A.CUSAD1 = B.CUSTOMER_ADDRESS_1,
A.CUSAD2 = B.CUSTOMER_ADDRESS_2,
A.CUSAD3 = B.CUSTOMER_ADDRESS_3,
A.CUSZIP = B.CUSTOMER_ZIP_CODE,
A.CUSZPE = B.CUSZPE_NOT_USED,
A.CUSSTC = B.CUSTOMER_STATE,
A.CUSARA = B.CUSTOMER_AREA_CODE,
A.CUSPHN = B.CUSTOMER_PHONE,
A.CUSB17 = B.CUSB17_NOT_USED,
A.CUSTMT = B.STATEMENT_PRINT_CODE,
A.CUSCRL = B.CREDIT_LIMIT,
A.CUSCRC = B.CREDIT_CODE,
A.CUSMCD = B.CUSMCD_NOT_USED,
A.CUSTX1 = B.TAX_RATE_1,
A.CUSTX2 = B.TAX_RATE_2,
A.CUSTXC = B.TAX_RATE,
A.CUSTXE = B.TAX_EXEMPT_ID,
A.CUSB48 = B.CUSB48_NOT_USED,
A.CUSMDT = B.MAINTENANCE_DATE,
A.CUSB20 = B.CUSB20_NOT_USED,
A.CSSRCH = B.SEARCH_FIELD,
A.CUSBRN = B.BRANCH_ID,
A.CUSDST = B.DISTRIBUTOR_NUMBER,
A.CUSB28 = B.CUSB28_NOT_USED
FROM
dbo.mcusmas A
INNER JOIN (
SELECT
RECORD_ID,
ACTIVE_CODE,
COMPANY_NUMBER,
CUSTOMER_NUMBER,
CUSTOMER_NAME,
CUSTOMER_ADDRESS_1,
CUSTOMER_ADDRESS_2,
CUSTOMER_ADDRESS_3,
CUSTOMER_ZIP_CODE,
CUSZPE_NOT_USED,
CUSTOMER_STATE,
CUSTOMER_AREA_CODE,
CUSTOMER_PHONE,
CUSB17_NOT_USED,
STATEMENT_PRINT_CODE,
CREDIT_LIMIT,
CREDIT_CODE,
CUSMCD_NOT_USED,
TAX_RATE_1,
TAX_RATE_2,
TAX_RATE,
TAX_EXEMPT_ID,
CUSB48_NOT_USED,
MAINTENANCE_DATE,
CUSB20_NOT_USED,
SEARCH_FIELD,
BRANCH_ID,
DISTRIBUTOR_NUMBER,
CUSB28_NOT_USED,
FROM remoteserver.MCUSMASPLG
WHERE Event_State_ID = '*New' AND SENT_TO_DATA_WAREHOUSE = 'N'
) B
ON A.CUSMNB = B.CUSTOMER_NUMBER

There is no "change 1" or "change 2". There are only rows and SQL Server arbitrarily ends up using one of them. If you want to control the rows, you should select the one you want in advance:
FROM dbo.mcusmas A JOIN
(SELECT B.*,
ROW_NUMBER() OVER (PARTITION BY CUSTOMER_NUMBER ORDER BY <ordering col>) as seqnum
FROM remoteserver.MCUSMASPLG
WHERE Event_State_ID = '*New' AND
SENT_TO_DATA_WAREHOUSE = 'N'
) B
ON A.CUSMNB = B.CUSTOMER_NUMBER
I don't know how you are determining which row is the right one. Presumably, some column has this information and you can use it in the ORDER BY.

Related

Oracle 11g - MERGE and error ORA-30926: unable to get a stable set of rows in the source tables

I have read lots of posts related to the Oracle (11g) error ORA-30926, and I've checked Oracle's documentation on the proper use of the merge statement.
Based on previous threads, I've changed my code to specify a distinct value in the using clause and that is the value I compare in the ON clause. But I still get the ORA-30926 error.
I've also tested the subquery in the USING clause and it returns data without any problem. I've created a temporary table containing only data that meets conditions in the WHERE clause of the USING statement and tried to run that and I still get the error. Both tables have data in them also.
I hope someone can spot something in my code that is incorrect or give me any recommendations on testing.
BEGIN
MERGE
INTO persons myTarget
USING (
select
distinct(USERID),
GIVENNAME,
INITIALS,
SN,
GENERATIONQUALIFIER,
TITLE,
DISPLAYNAME,
TELEPHONENUMBER,
FACSIMILETELEPHONENUMBER,
MOBILE,
OTHERTELEPHONE
from person_updates
WHERE
SN IS NOT NULL
AND LENGTH(SN) < 20
AND SUBSTR(USERID,0,2) IN (SELECT PLACEID FROM code_table)
AND (LENGTH(USERID) = 8 OR LENGTH(USERID) = 10)
) mySource
ON (myTarget.userid = mySource.USERID)
WHEN MATCHED THEN
UPDATE SET myTarget.first_name = UPPER(mySource.GIVENNAME),
myTarget.last_name = UPPER(mySource.SN),
myTarget.generation = UPPER(mySource.GENERATIONQUALIFIER),
myTarget.title = UPPER(mySource.TITLE),
myTarget.display_name = UPPER(mySource.DISPLAYNAME),
myTarget.phone_num = UPPER(mySource.TELEPHONENUMBER),
myTarget.fax_num = UPPER(mySource.FACSIMILETELEPHONENUMBER),
myTarget.mobile_num = UPPER(mySource.MOBILE),
myTarget.dsn_phone = UPPER(mySource.OTHERTELEPHONE);
END;
As correctly told by #shrek using distinct will give you distinct rows across combination of all the columns you have selected. I have used row_number analytical function to get distinct rows only based on userid.
Query:
BEGIN
MERGE
INTO persons myTarget
USING (
select * from(
select
row_number() over(partition by userid order by null) as rn,
USERID,
GIVENNAME,
INITIALS,
SN,
GENERATIONQUALIFIER,
TITLE,
DISPLAYNAME,
EMPLOYEETYPE,
TELEPHONENUMBER,
FACSIMILETELEPHONENUMBER,
MOBILE,
OTHERTELEPHONE
from person_updates
WHERE
SN IS NOT NULL
AND LENGTH(SN) < 20
AND SUBSTR(USERID,0,2) IN (SELECT PLACEID FROM code_table)
AND (LENGTH(USERID) = 8 OR LENGTH(USERID) = 10)) where rn = 1
) mySource
ON (myTarget.userid = mySource.USERID)
WHEN MATCHED THEN
UPDATE SET myTarget.first_name = UPPER(mySource.GIVENNAME),
myTarget.last_name = UPPER(mySource.SN),
myTarget.generation = UPPER(mySource.GENERATIONQUALIFIER),
myTarget.title = UPPER(mySource.TITLE),
myTarget.display_name = UPPER(mySource.DISPLAYNAME),
myTarget.dod_emp_type = UPPER(mySource.EMPLOYEETYPE),
myTarget.phone_num = UPPER(mySource.TELEPHONENUMBER),
myTarget.fax_num = UPPER(mySource.FACSIMILETELEPHONENUMBER),
myTarget.mobile_num = UPPER(mySource.MOBILE),
myTarget.dsn_phone = UPPER(mySource.OTHERTELEPHONE);
END;
Hope this will help.

MAX NOT WORKING IN SQL QUERY

I want the latest record to be retrieved by the following query....
but max is not working in the below query. All the rows are getting retrieved instead of the latest one
SELECT SV.SEGMENT1 TARGETED_INCENTIVE,
SIT.ANALYSIS_CRITERIA_ID,
SIT.OBJECT_VERSION_NUMBER OBJECT_VERSION_NUMBER,
ST.ID_FLEX_NUM,
SIT.DATE_FROM,
SIT.DATE_TO,
MAX (SIT.PERSON_ANALYSIS_ID)
FROM FND_ID_FLEX_STRUCTURES_TL STTL,
FND_ID_FLEX_STRUCTURES ST,
PER_PERSON_ANALYSES SIT,
PER_ANALYSIS_CRITERIA SV
WHERE 1 = 1
AND (STTL.ID_FLEX_STRUCTURE_NAME) LIKE
('%%Tare%')
AND STTL.LANGUAGE = USERENV ('LANG')
AND ST.ID_FLEX_CODE = STTL.ID_FLEX_CODE
AND ST.ID_FLEX_NUM = STTL.ID_FLEX_NUM
AND ST.ID_FLEX_NUM = SIT.ID_FLEX_NUM
AND ST.ID_FLEX_NUM = SV.ID_FLEX_NUM
AND TO_DATE (SIT.DATE_TO) IS NULL
AND SIT.ANALYSIS_CRITERIA_ID = SV.ANALYSIS_CRITERIA_ID
AND SIT.PERSON_ID = (SELECT PERSON_ID
FROM abc
WHERE ID = :AIN)
GROUP BY SV.SEGMENT1,
SIT.ANALYSIS_CRITERIA_ID,
STTL.ID_FLEX_STRUCTURE_NAME,
SIT.OBJECT_VERSION_NUMBER,
ST.ID_FLEX_NUM,
SIT.DATE_FROM,
SIT.DATE_TO;
Can anyone guide ?
I'm afraid that's not what MAX() does. MAX() is an aggregate function (though it can be used as a window [analytic] function), so when you get the MAX() of a particular column grouped by other columns, you will get distinct combinations of values for all those other columns.
I think you might want something like this:
SELECT targeted_incentive, analysis_criteria_id
, object_version_number, id_flex_num, date_from
, date_to, person_analysis_id
FROM (
SELECT sv.segment1 AS targeted_incentive
, sit.analysis_criteria_id
, sit.object_version_number
, st.id_flex_num
, sit.date_from
, sit.date_to
, sit.person_analysis_id
, RANK() OVER ( ORDER BY sit.person_analysis_id DESC ) rn
FROM fnd_id_flex_structures_tl sttl
, fnd_id_flex_structures st
, per_person_analyses sit
, per_analysis_criteria sv
WHERE sttl.id_flex_structure_name LIKE '%Tare%'
AND sttl.language = USERENV('LANG')
AND st.id_flex_code = sttl.id_flex_code
AND st.id_flex_num = sttl.id_flex_num
AND st.id_flex_num = sit.id_flex_num
AND st.id_flex_num = sv.id_flex_num
AND sit.date_to IS NULL
AND sit.analysis_criteria_id = sv.analysis_criteria_id
AND sit.person_id = ( SELECT person_id FROM abc
WHERE id = :AIN )
) WHERE rn = 1;
The RANK() window function will return the rank of each row ordered by the value of person_analysis_id in descending order. To get the maximum value, simply filter for rank = 1. Note that this will return more than one row in case of ties. If you want only one row, use ROW_NUMBER() in place of RANK().
Also note that I cleaned up the query a bit. You certainly don't need to use two % wildcards in a row in a LIKE, for example. You also definitely don't need the WHERE 1=1 condition.

Trying to Show The records with the most recent Date

Im trying to use the Over Partition to create a row number based on SupplierAccountNumber then Sort by DateTimeCreated and then only show record 1. my current script i get an error saying Invalid column name 'RowNum'??
I have a list of email addresses for suppliers which have multiple addresses, i only want to pick out the most recent email address. Is there a better way of doing it?
SELECT plsuppliercontact.plsuppliercontactid,
plsupplieraccount.supplieraccountnumber,
plsupplieraccount.supplieraccountname,
plsupplieraccount.supplieraccountshortname,
plsuppliercontactvalue.contactvalue,
syscontacttype.name,
Rownum = Row_number()
OVER(
partition BY plsupplieraccount.supplieraccountnumber
ORDER BY plsuppliercontactvalue.datetimecreated DESC)
FROM alops.dbo.plsupplieraccount PLSupplierAccount,
alops.dbo.plsuppliercontact PLSupplierContact,
alops.dbo.plsuppliercontactvalue PLSupplierContactValue,
alops.dbo.syscontacttype SYSContactType
WHERE plsupplieraccount.plsupplieraccountid =
plsuppliercontact.plsupplieraccountid
AND plsuppliercontactvalue.plsuppliercontactid =
plsuppliercontact.plsuppliercontactid
AND syscontacttype.syscontacttypeid =
plsuppliercontactvalue.syscontacttypeid
AND (( syscontacttype.name = 'E-mail Address' ))
AND rownum = 1;
You didn't specify with RDBMS you were using, but most of them only apply aliases after the query is executed.
One trick is to wrap the query in an another query that takes care of this condition. E.g.:
SELECT *
FROM (
SELECT plsuppliercontact.plsuppliercontactid,
plsupplieraccount.supplieraccountnumber,
plsupplieraccount.supplieraccountname,
plsupplieraccount.supplieraccountshortname,
plsuppliercontactvalue.contactvalue,
syscontacttype.name,
Rownum = Row_number()
OVER(
partition BY plsupplieraccount.supplieraccountnumber
ORDER BY plsuppliercontactvalue.datetimecreated DESC)
FROM alops.dbo.plsupplieraccount PLSupplierAccount,
alops.dbo.plsuppliercontact PLSupplierContact,
alops.dbo.plsuppliercontactvalue PLSupplierContactValue,
alops.dbo.syscontacttype SYSContactType
WHERE plsupplieraccount.plsupplieraccountid =
plsuppliercontact.plsupplieraccountid
AND plsuppliercontactvalue.plsuppliercontactid =
plsuppliercontact.plsuppliercontactid
AND syscontacttype.syscontacttypeid =
plsuppliercontactvalue.syscontacttypeid
AND (( syscontacttype.name = 'E-mail Address' ))
)
WHERE rownum = 1;
Use the MAX aggregate
SELECT plsuppliercontact.plsuppliercontactid,
plsupplieraccount.supplieraccountnumber,
plsupplieraccount.supplieraccountname,
plsupplieraccount.supplieraccountshortname,
plsuppliercontactvalue.contactvalue,
syscontacttype.name,
MAX(plsuppliercontactvalue.datetimecreated)
FROM alops.dbo.plsupplieraccount PLSupplierAccount,
alops.dbo.plsuppliercontact PLSupplierContact,
alops.dbo.plsuppliercontactvalue PLSupplierContactValue,
alops.dbo.syscontacttype SYSContactType
WHERE plsupplieraccount.plsupplieraccountid =
plsuppliercontact.plsupplieraccountid
AND plsuppliercontactvalue.plsuppliercontactid =
plsuppliercontact.plsuppliercontactid
AND syscontacttype.syscontacttypeid =
plsuppliercontactvalue.syscontacttypeid
AND (( syscontacttype.name = 'E-mail Address' ))
GROUP BY plsuppliercontact.plsuppliercontactid,
plsupplieraccount.supplieraccountnumber,
plsupplieraccount.supplieraccountname,
plsupplieraccount.supplieraccountshortname,
plsuppliercontactvalue.contactvalue,
syscontacttype.name

Adding number of days to date in DB2

I have a table that looks for a row with eff_dte = WK_BCC_DATES. WK_BCC_DATES is a variable computed in a PL1 program, now I need this computation done in one query so QMF will do the computation. The computation is WK_BCC_DATES = DTE1 + NO_DAYS.
SELECT SUBSTR(PARM_VALUE,1,10)
FROM BCD75DBA.BCCA6000 T60
WHERE T60.COUNTRY_CODE = '896'
AND T60.SUBSIDIARY_CODE = '01'
AND T60.PARM_NAME = 'BCC_DATES'
AND T60.EFF_DTE = (SELECT MAX(T60A.EFF_DTE)
FROM BCD75DBA.BCCA6000 T60A
WHERE T60A.COUNTRY_CODE = '896'
AND T60A.SUBSIDIARY_CODE = '01'
AND T60A.PARM_NAME = 'BCC_DATES')`
and
SELECT SUBSTR(PARM_VALUE,1,3)
FROM BCD75DBA.BCCA6000 T60
WHERE T60.COUNTRY_CODE = '896'
AND T60.SUBSIDIARY_CODE = '01'
AND T60.PARM_NAME = 'BCC_DAYS'
AND T60.EFF_DTE = (SELECT MAX(T60A.EFF_DTE)
FROM BCD75DBA.BCCA6000 T60A
WHERE T60A.COUNTRY_CODE = '896'
AND T60A.SUBSIDIARY_CODE = '01'
AND T60A.PARM_NAME = 'BCC_DAYS')`
I tried grouping the first query AS DTE1 and then the second AS NO_DAYS but I am having an error "not valid in the context it is used".
Please advise what else I can do. I am using DB2 v9. Thanks.
So, you want to combine these queries? That's actually pretty easy. It's a little hard to tell what was causing the error without your combined query, but you may have been putting the alias out of place.
(For future reference, your tables appear to be some for of an EAV - Entity Attribute Value. Use that as a search term for future queries)
I think a slight re-writing will help you out here:
WITH Most_Recent_Rows AS (SELECT parm_name, parm_value,
ROW_NUMBER() OVER(PARTITION BY parm_name
ORDER BY eff_dte DESC) AS rn
FROM BCD75DBA.BCCA6000
WHERE country_code = '896'
AND subsidiary_code = '01'
AND parm_name IN ('BCC_DAYS', 'BCC_DATES'))
SELECT CAST(SUBSTR(bcc_dates.parm_value, 1, 10) AS DATE) +
CAST(SUBSTR(bcc_days.parm_value, 1, 3) AS INTEGER) DAYS
FROM Most_Recent_Rows bcc_days
JOIN Most_Recent_Rows bcc_dates
ON bcc_dates.parm_name = 'BCC_DATES'
AND bcc_dates.rn = 1
WHERE bcc_days.parm_name = 'BCC_DAYS'
AND bcc_days.rn = 1
Incidentally, I think you were trying for something like the following:
SELECT bcc_days, bcc_dates
FROM (SELECT SUBSTR(rw.parm_value, 1, 3) AS bcc_days
FROM BCD75DBA.BCCA6000 rw
JOIN (SELECT country_code, subsidiary_code, parm_name, MAX(eff_date) AS eff_date
FROM BCD75DBA.BCCA6000
WHERE parm_name = 'BCC_DAYS'
GROUP BY country_code, subsidiary_code, parm_name) ref
ON ref.country_code = rw.country_code
AND ref.subsidiary_code = rw.subsidiary_code
AND ref.parm_name = rw.parm_name
AND ref.eff_date = rw.eff_date) bcc_days
CROSS JOIN (SELECT SUBSTR(rw.parm_value, 1, 10) AS bcc_dates
FROM BCD75DBA.BCCA6000 rw
JOIN (SELECT country_code, subsidiary_code, parm_name, MAX(eff_date) AS eff_date
FROM BCD75DBA.BCCA6000
WHERE parm_name = 'BCC_DATES'
GROUP BY country_code, subsidiary_code, parm_name) ref
ON ref.country_code = rw.country_code
AND ref.subsidiary_code = rw.subsidiary_code
AND ref.parm_name = rw.parm_name
AND ref.eff_date = rw.eff_date) bcc_dates
This is less ideal, due to the repletion in the sub-subqueries. This can be combined with the previous part of the answer (using a CTE for the GROUP BY, then joining twice). I'm not sure which approach will yield better performance actually.
Neither query has been tested, due to lack of sample data and desired results, and the fact I don't currently have an instance.

Updating Null values with non-nulls in SQL Server

I'm trying to go through a table and update currently null info if there is a previous entry that was not null. The code below is what I came up with but that's just updating every field so that every contact has the same fax number. Does someone who is smarter than me know what to do?
Thanks for the help.
UPDATE dbo.AAASTA_CONTACT
SET
CON_CSN = COALESCE(CRNT_RCD.CON_CSN, LST_RCD.CON_CSN),
CON_COMMENTS = COALESCE(CRNT_RCD.CON_COMMENTS, LST_RCD.CON_COMMENTS),
CON_EMAIL_ADDR = COALESCE(CRNT_RCD.CON_EMAIL_ADDR, LST_RCD.CON_EMAIL_ADDR),
CON_FAX_PH_NUM = COALESCE(CRNT_RCD.CON_FAX_PH_NUM, LST_RCD.CON_FAX_PH_NUM),
CON_HOME_PH_NUM = COALESCE(CRNT_RCD.CON_HOME_PH_NUM, LST_RCD.CON_HOME_PH_NUM),
CON_SEX_MF = COALESCE(CRNT_RCD.CON_SEX_MF, LST_RCD.CON_SEX_MF),
CON_PER_TITLE = COALESCE(CRNT_RCD.CON_PER_TITLE, LST_RCD.CON_PER_TITLE),
CON_WORK_PH_NUM = COALESCE(CRNT_RCD.CON_WORK_PH_NUM, LST_RCD.CON_WORK_PH_NUM),
CON_SUPPRESSMAILFL = COALESCE(CRNT_RCD.CON_SUPPRESSMAILFL, LST_RCD.CON_SUPPRESSMAILFL)
FROM
(
SELECT
CON.ALIAS_KEY,
CON.LAST_UPD,
CON.CON_CSN,
CON.CON_COMMENTS,
CON.CON_EMAIL_ADDR,
CON.CON_FAX_PH_NUM,
CON.CON_HOME_PH_NUM,
CON.CON_SEX_MF,
CON.CON_PER_TITLE,
CON.CON_WORK_PH_NUM,
CON.CON_SUPPRESSMAILFL,
RANK() OVER (PARTITION BY CON.ALIAS_KEY ORDER BY CON.LAST_UPD) AS UPD_RNK
FROM dbo.AAASTA_CONTACT CON
) CRNT_RCD
LEFT OUTER JOIN
(
SELECT
CON.ALIAS_KEY,
CON.LAST_UPD,
CON.CON_CSN,
CON.CON_COMMENTS,
CON.CON_EMAIL_ADDR,
CON.CON_FAX_PH_NUM,
CON.CON_HOME_PH_NUM,
CON.CON_SEX_MF,
CON.CON_PER_TITLE,
CON.CON_WORK_PH_NUM,
CON.CON_SUPPRESSMAILFL,
RANK() OVER (PARTITION BY CON.ALIAS_KEY ORDER BY CON.LAST_UPD) AS UPD_RNK
FROM dbo.AAASTA_CONTACT CON
) LST_RCD
ON CRNT_RCD.ALIAS_KEY = LST_RCD.ALIAS_KEY
WHERE CRNT_RCD.UPD_RNK=1
AND LST_RCD.UPD_RNK>1