Oracle SQL- Flag records based on record's date vs history - sql

This is my 1st post on the forum. Usually I was able to find what I needed - but to tell the truth - I am not really sure how to ask a correct question to the issue. Therefore, please accept my apologies if there already is an answer on the forum and I missed it.
I am running the following code in an Oracle database via Benthic Software:
SELECT
T1."REGION"
, T1."COUNTRY"
, T1."IDNum"
, T1."CUSTOMER"
, T1."BUSSINESS"
, T3."FISCALYEARMONTH"
, T3."FISCALYEAR"
, SUM(T4."VALUE")
,"HISTORICAL_PURCHASE_FLAG"
FROM
"DATABASE"."SALES" T4
, "DATABASE"."CUSTOMER" T1
, "DATABASE"."PRODUCT" T2
, "DATABASE"."TIME" T3
WHERE
T4."CUSTOMERID" = T1."CUSTOMERID"
AND T4."PRODUCTID" = T2."PRODUCTID"
AND T4."DATEID" = T3."DATEID"
AND T3."FISCALYEAR" IN ('2016')
AND T1."COUNTRY" IN ('ENGLAND', 'France')
GROUP BY
T1."REGION"
, T1."COUNTRY"
, T1."IDNum"
, T1."CUSTOMER"
, T1."BUSSINESS"
, T3."FISCALYEARMONTH"
, T3."FISCALYEAR"
;
This query provides me with information on transactions. As you can see above, I would like to add a column named "HISTORICAL_PURCHASE_FLAG".
I would like the query to take CUSTOMER and FISCALYEARMONTH. Then, I would like to check if there are any transactions registered for the CUSTOMER, up to 2 years in the past.
So lets say I get the following result:
LineNum REGION COUNTRY IDNum CUSTOMER BUSSINESS FISCALYEARMONTH FISCALYEAR VALUE HISTORICAL_PURCHASE_FLAG
1 Europe ENGLAND 255 Abraxo Cleaner Co. Chemicals 201605 2016 34,567.00
2 Europe FRANCE 123 Metal Trade Heavy 201602 2016 12,500.00
3 Europe ENGLAND 255 Abraxo Cleaner Co. Chemicals 201601 2016 8,400.00
LineNum 1 shows transaction for Abraxo Cleaner Co. registered on 201605. And LineNum 3 is also for Abraxo Cleaner Co. but registered on 201601. What I would need the query to do, is to flag LineNum 1 as 'Existing'. Because there was a previous transaction registered.
On the other hand, LineNum 3 was the first time transactions was registered for Abraxo Cleaner Co. so the line would be flagged as 'New'.
To sum up, I would like for each row of data to be treated individually. And to check if there are any previous records of data for CUSTOMER & FISCALYEARMONTH - 24 months.
Thank you in advance for the help.

You can use LAG function:
SELECT
"REGION"
, "COUNTRY"
, "IDNum"
, "CUSTOMER"
, "BUSSINESS"
, "FISCALYEARMONTH"
, "FISCALYEAR"
, SUM("VALUE")
, MAX(CASE WHEN to_date(prev_fym,'YYYYMM') >= ADD_MONTHS (to_date("FISCALYEARMONTH",'YYYYMM'), -24) THEN 'Existing'
ELSE NULL END) "HISTORICAL_PURCHASE_FLAG"
FROM
(
SELECT
T1."REGION"
, T1."COUNTRY"
, T1."IDNum"
, T1."CUSTOMER"
, T1."BUSSINESS"
, T3."FISCALYEARMONTH"
, T3."FISCALYEAR"
, T4."VALUE"
, LAG ("FISCALYEARMONTH", 1) OVER (PARTITION BY T1."IDNum" ORDER BY T3."FISCALYEARMONTH" DESC) prev_fym
FROM
"DATABASE"."SALES" T4
, "DATABASE"."CUSTOMER" T1
, "DATABASE"."PRODUCT" T2
, "DATABASE"."TIME" T3
WHERE
T4."CUSTOMERID" = T1."CUSTOMERID"
AND T4."PRODUCTID" = T2."PRODUCTID"
AND T4."DATEID" = T3."DATEID"
AND T1."COUNTRY" IN ('ENGLAND', 'France')
AND T3."FISCALYEAR" IN ('2014','2015','2016')
)
WHERE "FISCALYEAR" IN ('2016')
GROUP BY
"REGION"
, "COUNTRY"
, "IDNum"
, "CUSTOMER"
, "BUSSINESS"
, "FISCALYEARMONTH"
, "FISCALYEAR"
;

Using a simplified "input" table... You can use the LAG() analytic function and a comparison condition to populate your last column. I assume your fiscalyearmonth is a number - if it is a character field, wrap fiscalyearmonth within TO_NUMBER(). (It would be much better if in fact you stored these as true Oracle dates, perhaps date 2016-06-01 instead of 201606, but I worked with what you have currently... and took advantage that in numeric format, "24 months ago" simply means "subtract 200").
with inputs (linenum, idnum, fiscalyearmonth) as (
select 1, 255, 201605 from dual union all
select 2, 123, 201602 from dual union all
select 3, 255, 201601 from dual union all
select 4, 255, 201210 from dual
)
select linenum, idnum, fiscalyearmonth,
case when fiscalyearmonth
- lag(fiscalyearmonth)
over (partition by idnum order by fiscalyearmonth) < 200
then 'Existing' else 'New' end as flag
from inputs
order by linenum;
LINENUM IDNUM FISCALYEARMONTH FLAG
---------- ---------- --------------- --------
1 255 201605 Existing
2 123 201602 New
3 255 201601 New
4 255 201210 New

Another solution might be to outer Join "DATABASE"."SALES" T4 a second time as T5, wilter the fiscal year via WHERE to < t4.FiscalYear-2. If the Column is NULL, the record is new, if the outer join results in a value, the record is historic.

You can achieve using row_number() function as below... modify as per your need...I assumed 2 years (means previous 24 months from sysdate ).
You can run the sub-queries separately to check how its working.
Select
"REGION"
,"COUNTRY"
,"IDNum"
,"CUSTOMER"
,"BUSSINESS"
,"FISCALYEARMONTH"
,"FISCALYEAR"
,"VALUE"
, ( case when ( TXNNO = 1 or TOTAL_TXN_LAST24MTH = 0 ) then 'New' else 'Existing' end ) as "HISTORICAL_PURCHASE_FLAG" -- if no txn in last 24 month or its first txn then 'new' else 'existing'
from
(
select
SubQry."REGION"
, SubQry."COUNTRY"
, SubQry."IDNum"
, SubQry."CUSTOMER"
, SubQry."BUSSINESS"
, SubQry."FISCALYEARMONTH"
, SUBQRY."FISCALYEAR"
, SUBQRY."VALUE"
, ROW_NUMBER() over (partition by SUBQRY."REGION",SUBQRY."COUNTRY",SUBQRY."IDNum",SUBQRY."CUSTOMER",SUBQRY."BUSSINESS" order by SUBQRY."FISCALYEARMONTH") as TXNNO,
, SUM(case when (TO_NUMBER(TO_CHAR(sysdate,'YYYYMM')) - SUBQRY."FISCALYEARMONTH") < 24 then 1 else 0 end) as TOTAL_TXN_LAST24MTH
From
(
SELECT
T1."REGION"
, T1."COUNTRY"
, T1."IDNum"
, T1."CUSTOMER"
, T1."BUSSINESS"
, T3."FISCALYEARMONTH"
, T3."FISCALYEAR"
, SUM(T4."VALUE") as VALUE
FROM
"DATABASE"."SALES" T4
, "DATABASE"."CUSTOMER" T1
, "DATABASE"."PRODUCT" T2
, "DATABASE"."TIME" T3
WHERE
T4."CUSTOMERID" = T1."CUSTOMERID"
AND T4."PRODUCTID" = T2."PRODUCTID"
AND T4."DATEID" = T3."DATEID"
AND T3."FISCALYEAR" IN ('2016')
AND T1."COUNTRY" IN ('ENGLAND', 'France')
GROUP BY
T1."REGION"
, T1."COUNTRY"
, T1."IDNum"
, T1."CUSTOMER"
, T1."BUSSINESS"
, T3."FISCALYEARMONTH"
, T3."FISCALYEAR"
) SUBQRY
);

Related

Total customer per reporting date without union

I would like to display to run this report where I show the total number of customers per reporting date. Here is a how I need the data to look like:
My original dataset look like this (please see query): In order to calculate the number of customers. I need to use the start and end date: if Start_Date>reporting_date and End_Date<=reporting_date then count as a customer.
I was able to develop a script, but it only gives me the total number of customers for only one reporting date.
select '2022-10-31' reporting_date, count(case when Start_Date>'2022-10-31' and End_Date<='2022-10-31' then Customer_ID end)
from (values ('2022-10-14','2022-8-19','0010Y654012P6KuQAK')
, ('2022-3-15','2022-9-14','0011v65402PoSpVAAV')
, ('2021-1-11','2022-10-11','0010Y654012P6DuQAK')
, ('2022-12-1','2022-5-14','0011v65402u7muLAAQ')
, ('2021-1-30','2022-3-14','0010Y654012P6DuQAK')
, ('2022-10-31','2022-2-14','0010Y654012P6PJQA0')
, ('2021-10-31','US','0010Y654012P6PJQA0')
, ('2021-5-31','2022-5-14','0011v65402x8cjqAAA')
, ('2022-6-2','2022-1-13','0010Y654016OqkJQAS')
, ('2022-1-1','2022-11-11','0010Y654016OqIaQAK')
) a(Start_Date ,End_Date ,Customer_ID)
Is there a way to amend the code with cross-join or other workarounds to the total customers per reporting date without doing many unions
select '2022-10-31' reporting_date, count(case when Start_Date>'2022-10-31' and End_Date<='2022-10-31' then Customer_ID end)
from (values ('2022-10-14','2022-8-19','0010Y654012P6KuQAK')
, ('2022-3-15','2022-9-14','0011v65402PoSpVAAV')
, ('2021-1-11','2022-10-11','0010Y654012P6DuQAK')
, ('2022-12-1','2022-5-14','0011v65402u7muLAAQ')
, ('2021-1-30','2022-3-14','0010Y654012P6DuQAK')
, ('2022-10-31','2022-2-14','0010Y654012P6PJQA0')
, ('2021-10-31','US','0010Y654012P6PJQA0')
, ('2021-5-31','2022-5-14','0011v65402x8cjqAAA')
, ('2022-6-2','2022-1-13','0010Y654016OqkJQAS')
, ('2022-1-1','2022-11-11','0010Y654016OqIaQAK')
) a(Start_Date ,End_Date ,Customer_ID)
UNION ALL
select '2022-9-30' reporting_date, count(case when Start_Date>'2022-9-301' and End_Date<='2022-9-30' then Customer_ID end)
from (values ('2022-10-14','2022-8-19','0010Y654012P6KuQAK')
, ('2022-3-15','2022-9-14','0011v65402PoSpVAAV')
, ('2021-1-11','2022-10-11','0010Y654012P6DuQAK')
, ('2022-12-1','2022-5-14','0011v65402u7muLAAQ')
, ('2021-1-30','2022-3-14','0010Y654012P6DuQAK')
, ('2022-10-31','2022-2-14','0010Y654012P6PJQA0')
, ('2021-10-31','US','0010Y654012P6PJQA0')
, ('2021-5-31','2022-5-14','0011v65402x8cjqAAA')
, ('2022-6-2','2022-1-13','0010Y654016OqkJQAS')
, ('2022-1-1','2022-11-11','0010Y654016OqIaQAK')
) a(Start_Date ,End_Date ,Customer_ID)
It is possible to provide date ranges as a separate table/subquery, join to the actual data and perform grouping:
select s.start_d, s.end_d, COUNT(Customer_ID) AS total
FROM (SELECT '2022-10-31'::DATE, '2022-10-31'::DATE
UNION SELECT '2022-09-30', '2022-09-30')
AS s(start_d, end_d)
LEFT JOIN (values ('2022-10-14','2022-8-19','0010Y654012P6KuQAK')
, ('2022-3-15','2022-9-14','0011v65402PoSpVAAV')
, ('2021-1-11','2022-10-11','0010Y654012P6DuQAK')
, ('2022-12-1','2022-5-14','0011v65402u7muLAAQ')
, ('2021-1-30','2022-3-14','0010Y654012P6DuQAK')
, ('2022-10-31','2022-2-14','0010Y654012P6PJQA0')
, ('2021-10-31','2021-10-31','0010Y654012P6PJQA0')
, ('2021-5-31','2022-5-14','0011v65402x8cjqAAA')
, ('2022-6-2','2022-1-13','0010Y654016OqkJQAS')
, ('2022-1-1','2022-11-11','0010Y654016OqIaQAK')
) a(Start_Date ,End_Date ,Customer_ID)
ON a.Start_Date>s.start_d and a.End_Date<=s.end_d
GROUP BY s.start_d, s.end_d;
Output:

CTE with paging returning random value from subquery

I have a table with contact(contacts) information and another relationship table with key/value data for the contact table (customdata), the custom data rows are not unique, they can repeat, and they have a creation date as well.
I have a CTE querying the contacts, pretty simple, but I also want to return subquery column with from particular key, and the value from this query happens to contain a date, stored as varchar, and since this table does not contain unique rows I'm using TOP 1 and sorting by the row creation date.
The issue I'm having is that the value from the custom data table is returning random values and to top it off its not sorting correctly when casted to date.
WITH Customers_CTE AS(
SELECT row_number() over (ORDER BY (SELECT TOP 1 CONVERT(DATETIME, data_value, 101)
FROM CustomData
WHERE (data_cust_id = Customers.cust_id AND data_key = 'Sign Date' AND ISDATE(data_value) = 1) ORDER BY data_created DESC) DESC) AS rowNum,
COUNT(*) OVER (PARTITION BY NULL) AS [RowCount],
Customers.FirstName,
Customers.LastName,
(SELECT TOP 1 CONVERT(DATETIME, data_value, 101)
FROM CustomData
WHERE (data_cust_id = Customers.cust_id AND data_key = 'Sign Date' AND ISDATE(data_value) = 1) ORDER BY data_created DESC) AS DateSigned
FROM Customers)
SELECT * FROM Customers_CTE
WHERE rowNum >= 0 and rowNum < 10
Data sample
CUSTOMERS
cust_id, cust_firstname, cust_lastname
--------------------------------------
1 , john , doe
2 , jane , mary
CUSTOM DATA
data_created, data_cust_id, data_key , data_value
------------------------------------------------------
2018-04-06 , 1 , 'Sign Date' , '2018-03-17'
2018-04-06 , 1 , 'Agreed' , 'Yes'
2019-03-12 , 1 , 'Renew Date' , '2019-01-25'
2020-04-11 , 2 , 'Sign Date' , '2020-03-28'
2020-04-11 , 2 , 'Agreed' , 'Yes'
2020-06-07 , 1 , 'Sign Date' , '2020-05-13'
2020-10-21 , 2 , 'Sign Date' , '2020-09-15'
RESULT
FirstName , LastName , DateSigned
-------------------------------------
jane , mary , 2020-09-15
john , doe , 2020-05-13
I'm struggling to see what's going on with your CTE or what you are trying to do, it's just wriddled with syntax errors or issues.
If this helps, to get your desired output from your sample data, you just need the following - if I've understood your sample data you just want the maximum date value for each customer Id for a particular "key" value:
select c.cust_firstname, c.cust_lastname, d.DateSigned
from customers c
outer apply (
select Max(Try_Convert(date,data_value)) DateSigned
from customdata cd
where cd.data_cust_id=c.cust_id and cd.data_key='Sign date'
)d

Month Aggregation with 0 for NULL results

I have seen something similar but I can't get this to work:
SELECT
CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY.CALENDAR_MONTH
, CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI.TSI_NOMINAL_CODE
, CNTRSINTDATA.DIM_CNTRS_TSI_COA.TSI_NOMINAL_ACC
, CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY.CNTRS_FIN_YEAR
, SUM (CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI.QB_TRANS_AMOUNT) AS CNTRS_ACC_BUDGET
FROM
CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI
, CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY
, CNTRSINTDATA.DIM_CNTRS_TSI_COA
WHERE
CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI.QB_TRANS_DATE = CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY.CALENDAR_DATE
AND CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY.CNTRS_FIN_YEAR LIKE '2017'
AND CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI.TSI_NOMINAL_CODE = CNTRSINTDATA.DIM_CNTRS_TSI_COA.TSI_NOMINAL_CODE
AND CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI.TSI_NOMINAL_CODE = '6598'
GROUP BY
CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI.TSI_NOMINAL_CODE
, CNTRSINTDATA.DIM_CNTRS_TSI_COA.TSI_NOMINAL_ACC
, CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY.CALENDAR_MONTH
, CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY.CNTRS_FIN_YEAR;`
The above query returns results for:
Feb-17 250
Jul-17 400
Jun-17 654
May-17 654
Oct-17 150
Nov-17 250
Aug-17 250
Sep-17
I need the rest of the months to also come back with zero's as there no transactions on the account that month.
Jan-17 0
Feb-17 250
Mar-17 0
Apr-17 0
Jul-17 400
Jun-17 654
May-17 654
Oct-17 150
Nov-17 250
Aug-17 250
Sep-17 0
Dec-17 0
There is a date table that has all the months as VARCHAR2 against date. Just cant get the right syntax. Can anyone help please?
Let's break that down and make it more manageable.
Firstly, translate that to SQL-92 join syntax and add some aliases
SELECT
dcde.CALENDAR_MONTH
, fqtt.TSI_NOMINAL_CODE
, dctc.TSI_NOMINAL_ACC
, dcde.CNTRS_FIN_YEAR
, SUM (fqtt.QB_TRANS_AMOUNT) AS CNTRS_ACC_BUDGET
FROM
CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI fqtt
INNER JOIN CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY dcde
ON fqtt.QB_TRANS_DATE = dcde.CALENDAR_DATE
INNER JOIN CNTRSINTDATA.DIM_CNTRS_TSI_COA dctc
ON AND fqtt.TSI_NOMINAL_CODE = dctc.TSI_NOMINAL_CODE
WHERE
dcde.CNTRS_FIN_YEAR LIKE '2017'
AND
fqtt.TSI_NOMINAL_CODE = '6598'
GROUP BY
fqtt.TSI_NOMINAL_CODE
, dctc.TSI_NOMINAL_ACC
, dcde.CALENDAR_MONTH
, dcde.CNTRS_FIN_YEAR;
Next, you said that there exists a date record (presumably in CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY), so swap that around
SELECT
dcde.CALENDAR_MONTH
, fqtt.TSI_NOMINAL_CODE
, dctc.TSI_NOMINAL_ACC
, dcde.CNTRS_FIN_YEAR
, SUM (fqtt.QB_TRANS_AMOUNT) AS CNTRS_ACC_BUDGET
FROM
CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY dcde
INNER JOIN CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI fqtt
ON fqtt.QB_TRANS_DATE = dcde.CALENDAR_DATE
INNER JOIN CNTRSINTDATA.DIM_CNTRS_TSI_COA dctc
ON AND fqtt.TSI_NOMINAL_CODE = dctc.TSI_NOMINAL_CODE
WHERE
dcde.CNTRS_FIN_YEAR LIKE '2017'
AND
fqtt.TSI_NOMINAL_CODE = '6598'
GROUP BY
fqtt.TSI_NOMINAL_CODE
, dctc.TSI_NOMINAL_ACC
, dcde.CALENDAR_MONTH
, dcde.CNTRS_FIN_YEAR;
Finally, you don't actually want inner joins because that eliminates tuples where there is no match. In this case, you want a left join because you want to have the left value even if no value exists on the right. You also need to coalesce your sum expression to 0 because (for reasons I cannot fathom), the SQL standard defines the sum of a bunch records only containing null as null.
SELECT
dcde.CALENDAR_MONTH
, fqtt.TSI_NOMINAL_CODE
, dctc.TSI_NOMINAL_ACC
, dcde.CNTRS_FIN_YEAR
, COALESCE(SUM (fqtt.QB_TRANS_AMOUNT), 0) AS CNTRS_ACC_BUDGET
FROM
CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY dcde
LEFT JOIN CNTRSINTDATA.FACT_QUICKBOOKS_TRANS_TSI fqtt
ON fqtt.QB_TRANS_DATE = dcde.CALENDAR_DATE
LEFT JOIN CNTRSINTDATA.DIM_CNTRS_TSI_COA dctc
ON AND fqtt.TSI_NOMINAL_CODE = dctc.TSI_NOMINAL_CODE
WHERE
dcde.CNTRS_FIN_YEAR LIKE '2017'
AND
fqtt.TSI_NOMINAL_CODE = '6598'
GROUP BY
fqtt.TSI_NOMINAL_CODE
, dctc.TSI_NOMINAL_ACC
, dcde.CALENDAR_MONTH
, dcde.CNTRS_FIN_YEAR;
Give that a try and see if it is what you want
#Adam G,
Thank you for your suggestion. I actually got an idea from your commentry and instead created a select statement to get all the months I needed first. After that I joined to a select statement picking up the transactions where months matched and it worked. See below (come table names have changed)
SELECT DISTINCT
CNTRS_DATE_ENTITY.CALENDAR_MONTH
, CNTRS_DATE_ENTITY.CNTRS_FIN_MONTH_POS
, COALESCE (TRANS_DATA.TRANS_AMOUNT,0) AS TRANS_AMOUNT
FROM
CNTRSINTDATA.DIM_CNTRS_DATE_ENTITY CNTRS_DATE_ENTITY
LEFT JOIN
(
SELECT
QBS_TRANS_TSI.QB_NOMINAL_CODE
, TO_CHAR(QBS_TRANS_TSI.QB_TRANS_DATE, 'Mon-YY') AS TRANS_MONTH
, COALESCE (SUM(QBS_TRANS_TSI.QB_TRANS_AMOUNT),0) AS TRANS _AMOUNT
FROM
CNTRSINTDATA.FACT_QBS_TRANS_TSI QBS_TRANS_TSI
WHERE
QBS_TRANS_TSI.TSI_NOMINAL_CODE = '6598'
GROUP BY
QBS_TRANS_TSI.QB_NOMINAL_CODE
, TO_CHAR(QBS_TRANS_TSI.QB_TRANS_DATE, 'Mon-YY')
) TRANS_DATA
ON TRANS_DATA.TRANS_MONTH = CNTRS_DATE_ENTITY.CALENDAR_MONTH
WHERE
CNTRS_DATE_ENTITY.CNTRS_FIN_YEAR LIKE '2017'
ORDER BY
CNTRS_DATE_ENTITY.CNTRS_FIN_MONTH_POS
However the results have all the months but have blanks in the entity columns where the sum is zero. Any ideas on a remedy for this?
Here is one possible solution:
Generate fake empty transactions for the target period
for example using CTE:
declare #StartDate datetime = '20170101'
declare #EndDate datetime = '20171231'
;
with dt as
(
select #StartDate As 'thedate'
union all
select dateadd(month, 1, thedate) from dt where thedate < dateadd(month, -1, #EndDate)
)
select
dt.thedate 'row date',
datename(month,dt.thedate) 'Month',
YEAR(dt.thedate) 'Year',
0 'Amount '
from dt
Now you can add them to the transaction table (like union all) or you can do a left join with filtering on the result of your query.
I hope it helps! 🙂

Remove rows where one row offsets the next using accounting rules

I have a view of materials data which contains what was purchased and reversals of some of the purchases. I need a query that removes records that have reversals of purchase transactions. NOTE: The view does not have a primary key.
In the example I need to remove the first two rows as the second row offsets the first row because it reverses the purchase, but I need to keep the third row. Any ideas?
Here is the SQL for the view:
SELECT LEFT(mi.Plnt, 3) AS SBUID ,
oth.EQUIP AS PROJECTID ,
ms.Req_No AS GI ,
ms.Req_Item AS GI_LINE ,
CONVERT(VARCHAR(11), [Doc_Date], 100) + ' 12:00 AM' AS DOC_DATE ,
mi.[SLoc] AS SLOC ,
[Material] AS MATERIAL ,
mi.[Description] AS MATERIAL_DESCRIPTION ,
[Qty] AS QUANTITY ,
mi.[UoM] AS UOM ,
CASE WHEN mi.Mvt IN ( '101', '103', '105', '123', '261' ) THEN
mi.Amount
ELSE mi.Amount * -1
END AS Cost ,
mi.Amount AS EXT_ORG_COST ,
mi.PO AS [PO] ,
mi.Batch ,
mi.Vendor AS VENDOR ,
mi.VendorName AS VENDOR_NAME ,
at.AC_Group AS AC_TYPE ,
[Mvt] AS MVT
FROM [dbo].[MatIssued] mi
INNER JOIN dbo.OrderTableHistory oth ON oth.SUB_ORDER = mi.SubOrder
INNER JOIN dbo.Aircraft_Information2 ai ON ai.Equip = oth.EQUIP
INNER JOIN dbo.RFC_AcftTypeList at ON at.ID = ai.AC_TypeID
LEFT OUTER JOIN dbo.MatStatus ms ON ms.MPN = mi.Material
AND ms.SubOrder = mi.SubOrder
WHERE mi.Plnt IN ( '9131', '9132' )
AND mi.Mvt IN ( '101', '102', '103', '104', '105', '106', '122', '123' ,
'261' ,'262' )
AND mi.Doc_Date >= DATEADD(YEAR, -1, GETDATE())
ORDER BY mi.PO ,
mi.Batch ,
PROJECTID ,
mi.Mvt;
Some assumptions, based on your screenshot:
Reversals have same DOC_DATE as purchases
Reversals have same Batch as purchases
If the above assumptions are correct, try something like this:
DELETE FROM t
FROM MyTable t
WHERE EXISTS (
SELECT 1
FROM MyTable t2
WHERE
-- Join to outer table
t2.SLOC = t.SLOC
AND t2.MATERIAL = t.MATERIAL
AND t2.QUANTITY = t.QUANTITY
AND t2.PO = t.PO
AND t2.Batch = t.Batch
AND t2.VENDOR = t.VENDOR
GROUP BY SLOC, MATERIAL, QUANTITY, PO, Batch, VENDOR
HAVING COUNT(*) = 2 -- There are 2 matching rows
AND -MIN(QUANTITY) = MAX(QUANTITY) -- Minimum quantity negates Maximum quantity
AND MIN(COST) + MAX(COST) = 0 -- Costs cancel each other out
AND MIN(CASE WHEN Cost > 0 THEN DOC_DATE END) <= MIN(CASE WHEN Cost < 0 THEN DOC_DATE END) -- Purchase DOC_DATE less than or equal to reversal DOC_DATE
AND MIN(MVT) = MAX(MVT) + 1 -- Correlate purchase and reversal movement
AND (t.DOC_DATE = MIN(DOC_DATE) OR t.DOC_DATE = MAX(DOC_DATE)) -- Join to outer table
)

SQL query help to generate data

Below the query I created to get certain itemnumbers, qty ordered and price and others from the database. The problem is that sometimes an order doesn't contain 20 itemsnumbers but only 2. Now my question is if it's possible to fill the spaces with other itemnumbers random from the DB. It doesn't need to be correct because it's just for testing.
So can anybody help?
select
t.*,
-- THE THREE SUMVAT VALUES BELOW ARE VERY IMPORTANT. THEY ARE ONLY CORRECT HOWEVER WHEN THERE ARE NO NULL VALUES INVOLVED IN THE MATH,
-- I.E. WHEN THERE ARE 20 ITEMS/QTYS/PRICES INVOLVED WITH A CERTAIN ORDER_NO
((t.QTY1*t.PRICE1)+(t.QTY2*t.PRICE2)+(t.QTY3*t.PRICE3)+(t.QTY4*t.PRICE4)+(t.QTY5*t.PRICE5)) SUMVAT0, -- example: 5123.45 <- lines 1-5: Q*P
((t.QTY6*t.PRICE6)+(t.QTY7*t.PRICE7)+(t.QTY8*t.PRICE8)+(t.QTY9*t.PRICE9)+(t.QTY10*t.PRICE10)+(t.QTY11*t.PRICE11)+(t.QTY12*t.PRICE12)+(t.QTY13*t.PRICE13)+(t.QTY14*t.PRICE14)+(t.QTY15*t.PRICE15))
SUMVAT6, -- example: 1234.56 <- lines 6-15: Q*P
((t.QTY16*t.PRICE16)+(t.QTY17*t.PRICE17)+(t.QTY18*t.PRICE18)+(t.QTY19*t.PRICE19)+(t.QTY20*t.PRICE20)) SUMVAT19 -- example: 4567.89 <- lines 16-20: Q*P
from (
select
(to_char(p.vdate, 'YYYYMMDD') || to_char(sysdate, 'HH24MISS')) DT,
(to_char(p.vdate, 'YYYY-MM-DD') ||'T' || to_char(sysdate, 'HH24:MI:') || '00') DATETIME,
(to_char(orh.written_date, 'YYYY-MM-DD') ||'T00:00:00') DATETIME2,
orh.supplier FAKE_GLN,
y.*
from (
select
x.order_no ORDNO
, max(decode(r,1 ,x.item,null)) FAKE_GTIN1
, max(decode(r,2 ,x.item,null)) FAKE_GTIN2
, max(decode(r,3 ,x.item,null)) FAKE_GTIN3
, max(decode(r,4 ,x.item,null)) FAKE_GTIN4
, max(decode(r,5 ,x.item,null)) FAKE_GTIN5
, max(decode(r,6 ,x.item,null)) FAKE_GTIN6
, max(decode(r,7 ,x.item,null)) FAKE_GTIN7
, max(decode(r,8 ,x.item,null)) FAKE_GTIN8
, max(decode(r,9 ,x.item,null)) FAKE_GTIN9
, max(decode(r,10,x.item,null)) FAKE_GTIN10
, max(decode(r,11,x.item,null)) FAKE_GTIN11
, max(decode(r,12,x.item,null)) FAKE_GTIN12
, max(decode(r,13,x.item,null)) FAKE_GTIN13
, max(decode(r,14,x.item,null)) FAKE_GTIN14
, max(decode(r,15,x.item,null)) FAKE_GTIN15
, max(decode(r,16,x.item,null)) FAKE_GTIN16
, max(decode(r,17,x.item,null)) FAKE_GTIN17
, max(decode(r,18,x.item,null)) FAKE_GTIN18
, max(decode(r,19,x.item,null)) FAKE_GTIN19
, max(decode(r,20,x.item,null)) FAKE_GTIN20
, max(decode(r,1 ,x.qty_ordered,null)) QTY1
, max(decode(r,2 ,x.qty_ordered,null)) QTY2
, max(decode(r,3 ,x.qty_ordered,null)) QTY3
, max(decode(r,4 ,x.qty_ordered,null)) QTY4
, max(decode(r,5 ,x.qty_ordered,null)) QTY5
, max(decode(r,6 ,x.qty_ordered,null)) QTY6
, max(decode(r,7 ,x.qty_ordered,null)) QTY7
, max(decode(r,8 ,x.qty_ordered,null)) QTY8
, max(decode(r,9 ,x.qty_ordered,null)) QTY9
, max(decode(r,10,x.qty_ordered,null)) QTY10
, max(decode(r,11,x.qty_ordered,null)) QTY11
, max(decode(r,12,x.qty_ordered,null)) QTY12
, max(decode(r,13,x.qty_ordered,null)) QTY13
, max(decode(r,14,x.qty_ordered,null)) QTY14
, max(decode(r,15,x.qty_ordered,null)) QTY15
, max(decode(r,16,x.qty_ordered,null)) QTY16
, max(decode(r,17,x.qty_ordered,null)) QTY17
, max(decode(r,18,x.qty_ordered,null)) QTY18
, max(decode(r,19,x.qty_ordered,null)) QTY19
, max(decode(r,20,x.qty_ordered,null)) QTY20
, max(decode(r,1 ,x.unit_cost,null)) PRICE1
, max(decode(r,2 ,x.unit_cost,null)) PRICE2
, max(decode(r,3 ,x.unit_cost,null)) PRICE3
, max(decode(r,4 ,x.unit_cost,null)) PRICE4
, max(decode(r,5 ,x.unit_cost,null)) PRICE5
, max(decode(r,6 ,x.unit_cost,null)) PRICE6
, max(decode(r,7 ,x.unit_cost,null)) PRICE7
, max(decode(r,8 ,x.unit_cost,null)) PRICE8
, max(decode(r,9 ,x.unit_cost,null)) PRICE9
, max(decode(r,10,x.unit_cost,null)) PRICE10
, max(decode(r,11,x.unit_cost,null)) PRICE11
, max(decode(r,12,x.unit_cost,null)) PRICE12
, max(decode(r,13,x.unit_cost,null)) PRICE13
, max(decode(r,14,x.unit_cost,null)) PRICE14
, max(decode(r,15,x.unit_cost,null)) PRICE15
, max(decode(r,16,x.unit_cost,null)) PRICE16
, max(decode(r,17,x.unit_cost,null)) PRICE17
, max(decode(r,18,x.unit_cost,null)) PRICE18
, max(decode(r,19,x.unit_cost,null)) PRICE19
, max(decode(r,20,x.unit_cost,null)) PRICE20
from (
select
rank() over (partition by oh.order_no order by ol.item asc) r,
oh.supplier,
oh.order_no,
oh.written_date,
ol.item,
ol.qty_ordered,
ol.unit_cost
from
ordhead oh
JOIN ordloc ol ON oh.order_no = ol.order_no
where
-- count(numrows) = 1500
not unit_cost is null
-- and ol.order_no in (6181,6121)
) x
group by x.order_no
) y
JOIN ordhead orh ON orh.order_no = y.ORDNO,
period p
) t
;
Without being able to really test this, you might try something like this. Replace the inline view 'x' with this:
FROM (
WITH q AS (
SELECT LEVEL r, TO_CHAR(TRUNC(dbms_random.value*1000,0)) item
, TRUNC(dbms_random.value*100,0) qty_ordered
, TRUNC(dbms_random.value*10,2) unit_cost
FROM dual CONNECT BY LEVEL <= 20
)
SELECT COALESCE(x1.r, q.r) r, supplier, order_no, written_date
, COALESCE(x1.item, q.item) item
, COALESCE(x1.qty_ordered, q.qty_ordered) qty_ordered
, COALESCE(x1.unit_cost, q.unit_cost) unit_cost
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY oh.order_no ORDER BY ol.item ASC) r
, oh.supplier
, oh.order_no
, oh.written_date
, ol.item
, ol.qty_ordered
, ol.unit_cost
FROM ordhead oh JOIN ordloc ol ON oh.order_no = ol.order_no
WHERE NOT unit_cost IS NULL) x1 RIGHT JOIN q ON x1.r = q.r
) x
GROUP BY x.order_no
The WITH clause will give you a table with 20 rows of random data. Outer join that with your old 'x' data and you will be guaranteed 20 rows of data. You might not need to cast the item as a varchar2 depending on data. (N.B., I finally found a query that it makes sense to use a RIGHT JOIN with. See this SO question)
I'm not quite sure what you're trying to do with the GROUP BY and MAX stuff? In the future it would be helpful to condense your examples into something others can easily test, a minimal case that gets your point across.
I also incorporated #Kevin's good suggestion to use ROW_NUMBER instead of RANK.
very difficult to understand...
i think you might be ok if you put a 0 instead of null in the price values...
, max(decode(r,18,x.unit_cost,0)) PRICE18
and
, max(decode(r,20,x.qty_ordered,0)) QTY20
then at least the math should work.
Rank will not guarantee a sequential count of the items in the groups there, may be gaps when you have several rows with the same value.
for a decent explanation see:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2920665938600
I think you need to use row_number