3

I have a table whose schema is like this:

CREATE TABLE product_shipping(
    product_id CHAR(10),
    product_name CHAR(10),
    deal_dt DATETIME,
    deal_reason_no SMALLINT,
    cancel_dt DATETIME,
    cancel_reason_no SMALLINT
);

Once the shipping date is arranged, the deal_dt and deal_reason_no will be set and cancel_dt and cancel_reason_no is null. However product_id is not unique in the table since the every product shipping can be cancelled. Once cancelled cancel_dt and cancel_reason_no will be set. If the cancelled product is bought by other customer then the shipping is recorded in the new row, and product_name can be changed in the new shipping.

I want to query the product_id and deal_dt whose deal_reason_no is 1, 3 or 5 and deal_dt is between '2014-04-01 00:00:00' and '2014-04-11 00:00:00' and the latest previous deal_dt and latest previous cancel_dt whose cancel_reason_no is 2 or 4 for the product_id.

I think the SQL could be like this:

SELECT 
C.product_id as product_id,
C.deal_dt as deal_dt,
R.deal_dt as previous_deal_dt,
R.cancel_dt as previous_cancel_dt,
FROM product_shipping C
LEFT JOIN product_shipping R ON (???)
WHERE TO_CHAR(C.deal_reason_no ) in ('1', '3', '5')
AND C.deal_dt between '2014-04-01 00:00:00' and '2014-04-11 00:00:00'

But I'm not sure how to join to get latest previous record.

I'm using Informix 11.70.

Edit: Add my idea. Is this reasonable?

SELECT 
C.product_id as product_id,
C.deal_dt as deal_dt,
MAX(R.deal_dt) as previous_deal_dt,
MAX(R.cancel_dt) as previous_cancel_dt,
FROM product_shipping C
LEFT JOIN product_shipping R ON (C.product_id = R.product_id and TO_CHAR(R.cancel_reason_no) MATCHES ('2', '4') and R.cancel_dt <= C.deal_dt)
WHERE TO_CHAR(C.deal_reason_no ) in ('1', '3', '5')
AND C.deal_dt between '2014-04-01 00:00:00' and '2014-04-11 00:00:00'
GROUP BY 1,2
0

5 Answers 5

2

I suggest you join to previous products that match criteria and (if any exist) narrow down to the one where no later row exists:

SELECT P.product_id AS product_id,
      ,P.deal_dt    AS deal_dt
      ,C.deal_dt    AS previous_deal_dt
      ,C.cancel_dt  AS previous_cancel_dt
FROM   product_shipping p
LEFT   JOIN product_shipping C ON C.product_id = P.product_id
                              AND C.cancel_reason_no IN (2, 4)
                              AND C.cancel_dt < P.deal_dt
WHERE  P.deal_reason_no IN (1, 3, 5)
AND    P.deal_dt BETWEEN '2014-04-01 00:00:00' AND '2014-04-11 00:00:00'
AND    NOT EXISTS (
   SELECT 1
   FROM   product_shipping C1
   WHERE  C1.product_id = C.product_id
   AND    C1.cancel_reason_no IN (2, 4)
   AND    C1.cancel_dt < P.deal_dt
   AND    C1.cancel_dt > C.deal_dt
   );

Assuming (product_id,cancel_dt) to be UNIQUE, else you need tiebreaker criteria to be unambiguous. This way you do not need GROUP BY, because only one (or no) row is left-joined.

Also assuming deal_dt and cancel_dt are supposed to come from the same cancelled deal.

This would be simpler with the window function lag(), but your version does not seem to support that according to the discussion under the duplicate post.

2

Assuming you have a recent enough version, infomix supports the LAG and LEAD functions over ranking operations, see http://www-01.ibm.com/support/knowledgecenter/SSGU8G_12.1.0/com.ibm.sqls.doc/ids_sqs_1513.htm

These will only work if the current row and the one you consider the previous row are find using the same filtering clauses though, otherwise you and up needing to reference the table three times: first to find the base row(s) you are interested in, next joined to that to find previous rows to consider, and third either a left outer join or correlated sub query referencing both to rule out extra rows if there may be several that match the "pervious row" criteria. Whether the sub query or extra join option is more efficient than the other will depend on your DBMS's query planner and your search criteria, so I suggest you try both with realistic test data to benchmark them.

For instance:

SELECT <things>
FROM sometable AS current
LEFT OUTER JOIN sometable AS older
ON older.refdate<current.refdate
AND <filters on older to find rows that match the "previous row" criteria>
LEFT OUTER JOIN some table AS middle
ON middle.refdate<current.refdate AND middle.refdate>older.refdate
AND <filters on middle to find rows that match the "previous row" criteria>
WHERE <filters to find current row or rows>
AND middle.refdate IS NULL

The null check on the middle take in the where clause instead of the join clause means that any combination where there is a more recent row than the one matched by the "older" join, so you know "current" is the current row and "older" is the most recent row before that which matches the "previous row" criteria.

If you know there will always be at least one previous row for each row found in "current" the join for "older" can be an inner join which may allow the query runner to be more efficient.

If you search criteria for the current and previous rows are the same (except the relative ages check of course) then using LAG() should be far more efficient as you don't need either of the other two references to the table.

0

First of all try to maintain a Order Id. This will act as the primary key and this is logical too as each order should have an id and this will uniquely identify each new order even if the product ordered is same.

Also each product has a unique product code or serial number.

I suggest you to make a primary key using two columns (OrderId,ProductSerialNumber)

Alter table product_shipping add column order_id varchar(10),product code varchar(10));

ALTER TABLE Product_shipping ADD CONSTRAINT pm_key PRIMARY KEY (OrderId,ProductSerialNumber)

Now when a product is returned ,use the order it and ill in the cancel date etc.

Also when the product is reordered, you can add a new row with new order id and same serial number.

This will help to track the order history for a particular item, who all ordered(using Serial number) And also you can add a cancel or shipping date,.

This makes data retrieval and maintenance easy.

1
  • The schema is not maintained by me so I cannot change that. Commented Jul 16, 2014 at 11:45
0

I'm not sure how to join to get latest previous record.

Start there. Get the latest previous record with a self-join:

select c.product_id, min(p.deal_dt) as prev_dt 
from product_shipping as c
join product_shipping as p
on  c.product_id = p.product_id
and c.deal_dt >= p.deal_dt
group by c.product_id

That will produce rows for which prev_dt = deal_dt, in the event that there is no prior record.

Join that to whatever, sprinkle WHERE as desired.

0

the last answer is useful. However, I wonder if it should not read max(p.deal_dt) to get the previous value?

select c.product_id, max(p.deal_dt) as prev_dt 
from product_shipping as c
join product_shipping as p
on  c.product_id = p.product_id
and c.deal_dt >= p.deal_dt
group by c.product_id

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.