SQL Command Not Properly Ended - Oracle Subquery - sql

I'm using a Crystal Reports 13 Add Command for record selection from an Oracle database connected through the Oracle 11g Client. The error I am receiving is ORA-00933: SQL command not properly ended, but I can't find anything the matter with my code (incomplete):
/* Determine units with billing code effective dates in the previous month */
SELECT "UNITS"."UnitNumber", "BILL"."EFF_DT"
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT OUTER JOIN "MFIVE"."VIEW_ALL_UNITS" "UNITS" ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
WHERE "UNITS"."OwnerDepartment" LIKE '580' AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
INNER JOIN
/* Loop through previously identified units and determine last billing code change prior to preious month */
(
SELECT "BILL2"."UNIT_ID", MAX("BILL2"."EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL2"
WHERE TO_CHAR("BILL2"."EFF_DT", 'MMYYYY') < TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
GROUP BY "BILL2"."UNIT_ID"
)
ON "BILL"."UNIT_ID" = "BILL2"."UNIT_ID"
ORDER BY "UNITS"."UnitNumber", "BILL"."EFF_DT" DESC
We are a state entity that leases vehicles (units) to other agencies. Each unit has a billing code with an associated effective date. The application is to develop a report of units with billing codes changes in the previous month.
Complicating the matter is that for each unit above, the report must also show the latest billing code and associated effective date prior to the previous month. A brief example:
Given this data and assuming it is now April 2016 (ordered for clarity)...
Unit Billing Code Effective Date Excluded
---- ------------ -------------- --------
1 A 04/15/2016 Present month
1 B 03/29/2016
1 A 03/15/2016
1 C 03/02/2016
1 B 01/01/2015
2 C 03/25/2016
2 A 03/04/2016
2 B 07/24/2014
2 A 01/01/2014 A later effective date prior to previous month exists
3 D 11/28/2014 No billing code change during previous month
The report should return the following...
Unit Billing Code Effective Date
---- ------------ --------------
1 B 03/29/2016
1 A 03/15/2016
1 C 03/02/2016
1 B 01/01/2015
2 C 03/25/2016
2 A 03/04/2016
2 B 07/24/2014
Any assistance resolving the error will be appreciated.

You have a WHERE clause before the INNER JOIN clause. This is invalid syntax - if you swap them it should work:
SELECT "UNITS"."UnitNumber",
"BILL"."EFF_DT"
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT OUTER JOIN
"MFIVE"."VIEW_ALL_UNITS" "UNITS"
ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
INNER JOIN
/* Loop through previously identified units and determine last billing code change prior to preious month */
(
SELECT "UNIT_ID",
MAX("EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT"
WHERE TO_CHAR("EFF_DT", 'MMYYYY') < TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
GROUP BY "UNIT_ID"
) "BILL2"
ON "BILL"."UNIT_ID" = "BILL2"."UNIT_ID"
WHERE "UNITS"."OwnerDepartment" LIKE '580'
AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
ORDER BY "UNITS"."UnitNumber", "BILL"."EFF_DT" DESC
Also, you need to move the "BILL2" alias outside the () brackets as you do not need the alias inside the brackets but you do outside.
Are you really sure you need the double-quotes ""? Double-quotes enforce case sensitivity in column names - the default behaviour is for Oracle to convert all table and column names to upper case to abstract the case-sensitivity from the user - since you are using both double-quotes and upper-case names the quotes seems redundant.

SOLUTION:
SELECT DISTINCT "BILL2".*
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT JOIN
/* Returns all rows for each unit with effective date >= maximum prior effective date and effective date < current month */
(
SELECT "UNITS"."UnitNumber" AS "UNITNO",
UPPER("UNITS"."SpecNoDescription") AS "TS_DESCR",
"UNITS"."UsingDepartment" AS "USING_DEPT",
"UNITS"."OwnerDepartment" AS "OWNING_DEPT",
"BILL"."BILLING_CODE",
"BILL"."EFF_DT",
"BILL"."UNIT_ID"
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT OUTER JOIN "MFIVE"."VIEW_ALL_UNITS" "UNITS" ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
WHERE TO_NUMBER(TO_CHAR(TRUNC("BILL"."EFF_DT"), 'YYYYMM'), '999999') < TO_NUMBER(TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, 0)), 'YYYYMM'), '999999') AND
"UNITS"."OwnerDepartment" = '580' AND
"BILL"."EFF_DT" >=
/* Returns maximum effective date prior to previous month for each unit */
(
SELECT MAX("BILL3"."EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL3"
WHERE "BILL3"."UNIT_ID" = "BILL"."UNIT_ID" AND
TO_NUMBER(TO_CHAR(TRUNC("BILL3"."EFF_DT"), 'YYYYMM'), '999999') < TO_NUMBER(TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, -1)), 'YYYYMM'), '999999')
GROUP BY "BILL3"."UNIT_ID"
)
) BILL2
ON "BILL"."UNIT_ID" = "BILL2"."UNIT_ID"
WHERE TO_CHAR("BILL"."EFF_DT", 'YYYYMM') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'YYYYMM')
ORDER BY "BILL2"."UNITNO", "BILL2"."EFF_DT" DESC

If the where before Join really matters to you, use a CTE. (Employing with clause for temporary table and joining on the same.)
With c as (SELECT "UNITS"."UnitNumber", "BILL"."EFF_DT","BILL"."UNIT_ID" -- Correction: Was " BILL"."UNIT_ID" (spacetanker)
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL" -- Returning unit id column too, to be used in join
LEFT OUTER JOIN "MFIVE"."VIEW_ALL_UNITS" "UNITS" ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
WHERE "UNITS"."OwnerDepartment" LIKE '580' AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY'))
select * from c --Filter out your required columns from c and d alias results, e.g c.UNIT_ID
INNER JOIN
--Loop through previously identified units and determine last billing code change prior to preious month */
(
SELECT "BILL2"."UNIT_ID", MAX("BILL2"."EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL2"
WHERE TO_CHAR("BILL2"."EFF_DT", 'MMYYYY') < TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
GROUP BY "BILL2"."UNIT_ID"
) d
ON c."UNIT_ID" = d."UNIT_ID"
order by c."UnitNumber", c."EFF_DT" desc -- COrrection: Removed semicolon that Crystal Reports didn't like (spacetanker)
It seems this query has lots of scope for tuning though. However, one who has access to the data and requirement specification is the best judge.
EDIT :
You are not able to see data PRIOR to previous month since you are using BILL.EFF_DT in your original question's select statement, which is filtered to give only dates of previous month(..AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY'))
If you want the data as you want, I guess you have to use the BILL2 section (d part in my subquery), by giving an alias to Max(EFF_DT), and using that alias in your select clause.

Related

SUM with left outer join gets inflated result

The following query gives me the MRR (monthly recurring revenue) for my customer:
with dims as (
select distinct subscription_id, country_name, product_name from revenue
where site_id = '18XLsHIVSJg' and subscription_id is not null
)
select to_date('2022-07-01') as occurred_date,
count(distinct srm.subscription_id) as subscriptions,
count(distinct srm.receiver_contact) as subscribers,
sum(srm.baseline_mrr) as mrr_srm
from subscription_revenue_mart srm
join dims d on d.subscription_id = srm.subscription_id
where srm.site_id = '18XLsHIVSJg'
-- MRR as of the day before ie June 30th
and to_date(srm.creation_date) < '2022-07-01'
-- Counting the subscriptions active after July 1st
and ((srm.subscription_status = 'SUBL.A') or
-- Counting the subscriptions canceled/deactivated after July 1st
(srm.subscription_status = 'SUBL.C' and (srm.deactivation_date >= '2022-07-01') or (srm.canceled_date >= '2022-07-01')) ) group by 1;
I get a total of $5922.15 but I need to add data from another table to capture upgrades/downgrades a customer makes on a product subscription. Using the same approach as above, I can query my "change" table thusly:
select subscription_id, sum(mrr_change_amount) mrr_change_amount,max(subscription_event_date) subscription_event_date from subscription_revenue_mart_change srmc
where site_id = '18XLsHIVSJg'
and to_date(srmc.creation_date) < '2022-07-01'
and ((srmc.subscription_status = 'SUBL.A')
or (srmc.subscription_status = 'SUBL.C' and (srmc.deactivation_date >= '2022-07-01') or (srmc.canceled_date >= '2022-07-01')))
group by 1;
I get a total of $3635.47
When I combine both queries into one, I get an inflated result:
with dims as (
select distinct subscription_id, country_name, product_name from revenue
where site_id = '18XLsHIVSJg' and subscription_id is not null
),
change as (
select subscription_id, sum(mrr_change_amount) mrr_change_amount,
-- there can be multiple changes per subscription
max(subscription_event_date) subscription_event_date from subscription_revenue_mart_change srmc
where site_id = '18XLsHIVSJg'
and to_date(srmc.creation_date) < '2022-07-01'
and ((srmc.subscription_status = 'SUBL.A')
or (srmc.subscription_status = 'SUBL.C' and (srmc.deactivation_date >= '2022-07-01') or (srmc.canceled_date >= '2022-07-01')))
group by 1
)
select to_date('2022-07-01') as occurred_date,
count(distinct srm.subscription_id) as subscriptions,
count(distinct srm.receiver_contact) as subscribers,
-- See comment RE: LEFT OUTER join
sum(coalesce(c.mrr_change_amount,srm.baseline_mrr)) as mrr
from subscription_revenue_mart srm
join dims d
on d.subscription_id = srm.subscription_id
-- LEFT OUTER join required for customers that never made a change
left outer join change c
on srm.subscription_id = c.subscription_id
where srm.site_id = '18XLsHIVSJg'
and to_date(srm.creation_date) < '2022-07-01'
and ((srm.subscription_status = 'SUBL.A')
or (srm.subscription_status = 'SUBL.C' and (srm.deactivation_date >= '2022-07-01') or (srm.canceled_date >= '2022-07-01'))) group by 1;
It should be $9557.62 ie (5922.15 + $3635.47) but the query outputs $16116.91, which is wrong.
I think the explode-implode syndrome may cause this.
I had designed my "change" CTE to prevent this by aggregating all the relevant fields but it's not working.
Can someone provide pointers on the best way to work around this issue?
It would help if you gave us sample data too, but I see a problem here:
sum(coalesce(c.mrr_change_amount,srm.baseline_mrr)) as mrr
Why COALESCE? That will give you one of the 2 numbers, but I guess what you want is:
sum(ifnull(c.mrr_change_amount, 0) + srm.baseline_mrr) as mrr
That's the best I can offer with what you've given us.

Access SQL statement: if no entries are valid, return the last one

I am trying to get a SQL statement which solves the following issue.
I have a table "calendar" which includes only one column "date". This table has 12 entries for each month in 2019 (01.31.2019, 02.28.2019 and so on). The second table "values" (which I get from an ERP system) has three columns, "from", "to" and "amount" (e.g. 01.01.2019, 06.30.2019, 50 and 08.01.2019, 08.31.2019, 100).
I have this simple statement which checks which entry is valid on the specific date:
SELECT Calendar.Date, Values.From, Values.To, Values.Amount
FROM Calendar, [Values]
WHERE Calendar.Date >= Values.From
AND Calendar.Date <= Values.To;
There is no valid entry (in the table "values") for July, September, October, November and December.
In the case there is no valid entry the last entry should be used. In July it would be 50 and for September, October ... it would be 100.
I tried subquery and left joins, but I never got the wanted result.
Has anybody an idea or better a solution for this issue. I appreciate any support
I think that you are looking for an additional join on the Values table, that will return the last entry before the current date. When the first (LEFT) JOIN does not succeed, you can use the result returned by the second one.
To locate the last entry before the current date, we can use a NOT EXISTS condition with a correlated subquery.
SELECT
c.Date,
Nz(v.From, v1.From) AS [From],
Nz(v.To, v1.To) AS [To],
Nz(v.Amount, v1.Amount) AS [Amount]
FROM Calendar AS c
LEFT JOIN [Values] AS v
ON c.Date >= v.From AND c.Date <= v.To
LEFT JOIN [Values] AS v1
ON v1.To < c.Date
AND NOT EXISTS (
SELECT 1 FROM [Values] v2 WHERE v2.To < c.Date AND v2.To > v1.To
)
PS : it's been a good practice for a long time in SQL to avoid old-school, implicit JOINs, and always use explicit JOINs.
You can do it with a LEFT JOIN and a subquery to get the last amount:
SELECT c.Date, v.From, v.To,
Nz(
v.Amount,
(SELECT MAX([Values].Amount) FROM [Values] WHERE [Values].From =
(SELECT MAX([Values].From) FROM [Values] WHERE [Values].From <= c.Date))
) AS Amount
FROM Calendar AS c LEFT JOIN [Values] AS v
ON c.Date>=v.From AND c.Date<=v.To;

JOIN on DATEPART month and year is causing extra rows

I have two tables that contain a date field. This date field is one of the JOIN causes that I would like to implement, but I only want to JOIN on the month and year, not the day. The # of records about triple when I attempt to do so. I'm guessing there is something wrong with my query? Or is this even possible? I'm using Postgres
SELECT a.load_date , a.mandt, a.vbeln,a.posnr, a.matnr, b.tfed
FROM tableA a
JOIN tableB b
ON date_part('month'::text, a.erdat) = date_part('month'::text, b.gdatu)
AND date_part('year'::text, a.erdat) = date_part('year'::text, b.gdatu)
EDIT Here is my full code
SELECT a.mandt, a.vbeln,
a.erdat, a.erzet, a.ernam, a.angdt, a.audat, a.vbtyp, a.trvog,
a.auart, a.submi, a.lifsk, a.faksk, a.netwr, a.waerk, a.vkorg, a.vtweg, a.spart,
a.vkgrp, a.vkbur, a.knumv, a.vdatu, a.vprgr, a.kalsm, a.vsbed, a.fkara, a.awahr,
a.bstnk, a.bstdk, a.telf1, a.kunnr, a.stafo, a.stwae, a.aedat, a.kvgr1,a.kvgr2,
a.kvgr3, a.kokrs, a.kkber, a.knkli, a.sbgrp, a.ctlpc, a.cmwae, a.cmfre, a.cmngv,
a.amtbl, a.hityp_pr, a.abrvw, a.vgbel, a.objnr, a.bukrs_vf, a.taxk1,a.xblnr,
a.vgtyp, a.abhod, a.abhov, a.stceg_l, a.landtx, a.fmbdat, a.vsnmr_v, a.handle,
a.yybcawv1, a.yybcawv2, a.yybcawv3, a.yyawv1dat, a.yyawv2dat, a.yybcawvc,
a.kvgr5, a.augru, a.autlf, a.bname, a.bnddt, a.bsark, a.cmnup, a.fiscalper,
a.fiscalyr, a.gwldt, a.ihrez, a.intind, a.intsum, a.rplnr, a.taxk2, a.yybabt,
a.yybemail, a.yybfax, a.yybname, a.yybphone, a.yyexporter, a.yypaypal_id,
a.yysd_projid, a.zone, a.zuonr, a.zz_campaign_id, a.zzedate, a.zzrev_cat_01,
a.zzrev_cat_02, a.zzrev_cat_03, a.zzrev_cat_04, a.zzrev_cat_05, a.zzrev_cat_06,
a.zzrev_cat_07, a.zzrev_cat_08, a.zzsdate, a.mahdt,
CASE
WHEN b.fcurr::text = 'USD'::text THEN a.netwr
WHEN b.fcurr::text = 'JPY'::text AND b.kurst::text = 'M'::text THEN a.netwr * b.ukurs / 10::numeric
WHEN b.fcurr::text = 'KRW'::text AND b.kurst::text = 'M'::text THEN a.netwr * b.ukurs / 10::numeric
WHEN b.kurst::text = 'M'::text THEN a.netwr * b.ukurs
ELSE a.netwr
END AS net_value_trans_currency_netwr
FROM src.sap_vbak a
JOIN src.sap_tcurr b
ON a.waerk::text = b.fcurr::text
AND date_part('MONTH'::text, a.erdat::timestamp with time zone) = date_part('MONTH'::text, b.gdatu::timestamp with time zone)
AND date_part('YEAR'::text, a.erdat::timestamp with time zone) = date_part('YEAR'::text, b.gdatu::timestamp with time zone);
I'm attempting to get currency conversions based off of the dates (month and year only) in each of the tables. Some of the currency conversion are different ( the CASE statement for net_value_trans_currency_netwr field). I am wanting the net_value_trans_currency_netwr field to be a new row that displays the currency conversion in USD. The original table has over 5 million rows. After the joins I end up with way more rows. From what I gather I'm getting a full join. How would I be able to execute what I'm trying to do without the full join creating more than needed rows?
You get duplicate rows as you are INNER JOINING on the month and year which are not unique. This is causing a cross join e.g.
Example Rows with dates
Date Month Year
1 01/01/2014 01 14
2 02/01/2014 01 14
Result of above join has 4 rows not 2!
1) Month from (1) Year from (1)
2) Month from (1) Year from (2)
3) Month from (2) Year from (1)
4) Month from (2) Year from (2)
If you want to avoid this you need something else to include in the join that makes each join unique! Adding the day may help but again if you have more than one date recorded on the same day you will get a duplicate. Have a think what else you could include on the join.
Use date_trunc() to simplify the query:
SELECT a.load_date, a.mandt, a.vbeln,a.posnr, a.matnr, b.tfed
FROM tableA a
JOIN tableB b ON date_trunc('month', a.erdat)
= date_trunc('month', b.gdatu);
Plus, you probably want to restrict the join further. This is a limited cross join resulting in a Cartesian product. If you have 3 rows for March 2014 in a tableA and 4 rows for March 2014 in a tableB, you already produce 12 rows in the result.

Get rows with difference of dates being one

I have the following table and rows defined in SQLFiddle
I need to select rows from products table where difference between two rows start_date and
nvl(return_date,end_date) is 1. i.e. start_date of current row and nvl(return_date,end_date) of previous row should be one
For example
PRODUCT_NO TSH098 and PRODUCT_REG_NO FLDG, the END_DATE is August, 15 2012 and
PRODUCT_NO TSH128 and PRODUCT_REG_NO FLDG start_date is August, 16 2012, so the difference is only of a day.
How can I get the desired output using sql.
Any help is highly appreciable.
Thanks
You can use lag analytical function to get access to a row at a given physical offset prior to the current position. According to your sorting order it might look like this(not so elegant though).
select *
from products p
join (select *
from(select p.Product_no
, p.Product_Reg_No
, case
when (lag(start_date, 1, start_date) over(order by product_reg_no)-
nvl(return_date, end_date)) = 1
then lag(start_date, 1, start_date)
over(order by product_reg_no)
end start_date
, End_Date
, Return_Date
from products p
order by 2,1 desc
)
where start_date is not null
) s
on (p.start_date = s.start_date or p.end_date = s.end_date)
order by 2, 1 desc
SQL FIddle DEMO
In SQL, date + X adds X days to the date. So you can:
select *
from products
where start_date + 1 = nvl(end_date, return_date)
If the dates could contain a time part, use trunc to remove the time part:
select *
from products
where trunc(start_date) + 1 = trunc(nvl(end_date, return_date))
Live example at SQL Fiddle.
I am under the impression you only want the matching dates differing by 1 day if the product reg no matches. So I simply joint it and I think this is what you want
select p1.product_reg_no,
p1.product_no product_no_1,
p2.product_no product_no_2,
p1.start_date start_date_1,
nvl(p2.return_date,p2.end_date) return_or_end_date_2
from products p1
join products p2 on (p1.product_reg_no = p2.product_reg_no)
where p1.start_date-1 = nvl(p2.return_date,p2.end_date)
SQL Fiddle
If I was wrong with the grouping then just leave the join condition away which with the given example products table brings the same result
select p1.product_reg_no,
p1.product_no product_no_1,
p2.product_no product_no_2,
p1.start_date start_date_1,
nvl(p2.return_date,p2.end_date) return_or_end_date_2
from products p1, products p2
where p1.start_date-1 = nvl(p2.return_date,p2.end_date)
SQL Fiddle 2
Now you say the difference is 1 day. I automatically assumed that start_date is 1 day higher than the nvl(return_date,end_date). Also I assumed that the date is always midnight. But to have all that also excluded you can work with trunc and go in both directions:
select p1.product_reg_no,
p1.product_no product_no_1,
p2.product_no product_no_2,
p1.start_date start_date_1,
nvl(p2.return_date,p2.end_date) return_or_end_date_2
from products p1, products p2
where trunc(p1.start_date)-1 = trunc(nvl(p2.return_date,p2.end_date))
or trunc(p1.start_date)+1 = trunc(nvl(p2.return_date,p2.end_date))
SQL Fiddle 3
And this all works because dates (not timestamp) can be calculated by adding and subtracting.
EDIT: Following your comment you want return_date or end_date to be compared and equal dates are also wanted:
select p1.product_reg_no,
p1.product_no product_no_1,
p2.product_no product_no_2,
p1.start_date start_date_1,
p2.return_date return_date_2,
p2.end_date end_date_2
from products p1, products p2
where trunc(p1.start_date) = trunc(p2.return_date)
or trunc(p1.start_date)-1 = trunc(p2.return_date)
or trunc(p1.start_date)+1 = trunc(p2.return_date)
or trunc(p1.start_date) = trunc(p2.end_date)
or trunc(p1.start_date)-1 = trunc(p2.end_date)
or trunc(p1.start_date)+1 = trunc(p2.end_date)
SQL Fiddle 4
The way to compare the current row with the previous row is to user the LAG() function. Something like this:
select * from
(
select p.*
, lag (end_date) over
(order by start_date )
as prev_end_date
, lag (return_date) over
(order by start_date )
as prev_return_date
from products p
)
where (trunc(start_date) - 1) = trunc(nvl(prev_return_date, prev_end_date))
order by 2,1 desc
However, this will not return the results you desire, because you have not defined a mechanism for defining a sort order. And without a sort order the concept of "previous row" is meaningless.
However, what you can do is this:
select p1.*
, p2.*
from products p1 cross join products p2
where (trunc(p2.start_date) - 1) = trunc(nvl(p1.return_date, p1.end_date))
order by 2, 1 desc
This SQL queries your table twice, filtering on the basis of dates. The each row in the result set contains a record from each table. If a given start_date matches more than one end_date or vice versa you will get records for multiple hits.
You mean like this?
SELECT T2.*
FROM PRODUCTS T1
JOIN PRODUCTS T2 ON (
nvl(T1.end_date, T1.return_date) + 1 = T2.start_date
);
In your SQL Fiddle example, it returns:
PRODUCT_NO PRODUCT_REG_NO START_DATE END_DATE RETURN_DATE
TSH128 FLDG August, 16 2012 00:00:00-0400 September, 15 2012 00:00:00-0400 (null)
TSH125 SCRW August, 08 2012 00:00:00-0400 September, 07 2012 00:00:00-0400 (null)
TSH137 SCRW September, 08 2012 00:00:00-0400 October, 07 2012 00:00:00-0400 (null)
TSH128 is returned for the reasons you already explained.
TSH125 is returned because TSH116 end_date is August, 07 2012.
TSH137 is returned because TSH125 end_date is September, 07 2012.
If you want to compare only rows within the same product_reg_no, it's easy to add that to the JOIN condition. If you want both "directions" of the 1-day difference, it's easy to add that too.

sql db2 select records from either table

I have an order file, with order id and ship date. Orders can only be shipped monday - friday. This means there are no records selected for Saturday and Sunday.
I use the same order file to get all order dates, with date in the same format (yyyymmdd).
i want to select a count of all the records from the order file based on order date... and (i believe) full outer join (or maybe right join?) the date file... because i would like to see
20120330 293
20120331 0
20120401 0
20120402 920
20120403 430
20120404 827
etc...
however, my sql statement is still not returning a zero record for the 31st and 1st.
with DatesTable as (
select ohordt "Date" from kivalib.orhdrpf
where ohordt between 20120315 and 20120406
group by ohordt order by ohordt
)
SELECT ohscdt, count(OHTXN#) "Count"
FROM KIVALIB.ORHDRPF full outer join DatesTable dts on dts."Date" = ohordt
--/*order status = filled & order type = 1 & date between (some fill date range)*/
WHERE OHSTAT = 'F' AND OHTYP = 1 and ohscdt between 20120401 and 20120406
GROUP BY ohscdt ORDER BY ohscdt
any ideas what i'm doing wrong?
thanks!
It's because there is no data for those days, they do not show up as rows. You can use a recursive CTE to build a contiguous list of dates between two values that the query can join on:
It will look something like:
WITH dates (val) AS (
SELECT CAST('2012-04-01' AS DATE)
FROM SYSIBM.SYSDUMMY1
UNION ALL
SELECT Val + 1 DAYS
FROM dates
WHERE Val < CAST('2012-04-06' AS DATE)
)
SELECT d.val AS "Date", o.ohscdt, COALESCE(COUNT(o.ohtxn#), 0) AS "Count"
FROM dates AS d
LEFT JOIN KIVALIB.ORDHRPF AS o
ON o.ohordt = TO_CHAR(d.val, 'YYYYMMDD')
WHERE o.ohstat = 'F'
AND o.ohtyp = 1