Calculate lead-time between selected rows (SQL) - sql

Given this table where we have users, the product that they used, and the first date that they used the product (I have also created a simple rank by user window). Note, each user will only have minimum 0 rows if they used nothing before, and 2 rows, if they used both products. There are only 3 products - cigars and beers.
How can I create a new view where each row is 1 user, the next column shows the first product, the next column shows the 2nd product, and the last column shows the lead-time b/w the first dates of use?

One method is conditional aggregation with row_number():
select user,
max(case when seqnum = 1 then product end) as product_1,
max(case when seqnum = 2 then product end) as product_2,
(max(case when seqnum = 2 then time_used end) -
max(case when seqnum = 1 then time_used end)
) as dif
from (select t.*,
row_number() over (partition by user order by time_used) as seqnum
from t
) t
group by user;
Date/time functions vary significantly across different databases. Not all support a simple -, so you might nee to adjust for your database.

Minus between dates may not work on each database
select
c1.user_id,
c1.first_product_used,
c2.second_product_used,
COALESCE(CAST((Cast(c2.second_date AS DATE) - Cast(c1.first_date AS DATE)) AS VARCHAR(20)), 'n/a') AS "leadtime_days"
from
(
select
user_id,
product_used AS first_product_used,
time_used AS first_date
from
check2
where
rank_of_use = 1
)c1
LEFT OUTER JOIN
(
select
user_id,
product_used AS second_product_used,
time_used AS second_date
from
check2
where
rank_of_use = 2
)c2
ON
c1.user_id = c2.user_id

Related

(HANA SQL) Show multiple values in one row

I am trying to complete the following:
Old situation
What I want
For a fixed maximum number of target columns, you can use window functions and conditional aggregation:
select customer,
max(case when rn = 1 then order_date end) as order_date_1,
max(case when rn = 2 then order_date end) as order_date_2,
max(case when rn = 3 then order_date end) as order_date_3
from (
select t.*, row_number() over(partition by customer order by order_date) rn
from mytable t
) t
group by customer

Organizing SQL data based on date

I am trying to organize my SQL data based off of the dates from which the orders were made.
My data:
SELECT DISTINCT ORDER_NO, ITEM, VERSION_NO,
(CASE WHEN ROW_NUMBER() OVER (PARTITION BY ORDER_NO ORDER BY NOT_BEFORE_DATE
ASC) = 1
THEN 'what-if'
ELSE 'wh'
END) AS VERSION_NEW
,
(CASE WHEN ROW_NUMBER() OVER (PARTITION BY ORDER_NO ORDER BY
NOT_BEFORE_DATE ASC) = 2
THEN 'initial'
ELSE 'other'
END) AS VERSION
FROM FDT_MAPTOOL
WHERE ITEM IN (1032711)
;
My results:
I want my data to be ordered by PO# and the date it was created.
As you can see in my picture the First two line have the same ITEM and same PO (Order_No). I need the first two to say Initial on the side because they are the first two based on the dates. They were created first. Everything after should say other.
I am not sure if PL/SQL is needed for this?
Thank you!
Use a different analytic function so that more than one row can have the value of 1 e.g.
SELECT DISTINCT ORDER_NO, ITEM, VERSION_NO,
(CASE WHEN DENSE_RANK() OVER (PARTITION BY ORDER_NO ORDER BY NOT_BEFORE_DATE
ASC) = 1
THEN 'what-if'
ELSE 'wh'
END) AS VERSION_NEW
,
(CASE WHEN DENSE_RANK() OVER (PARTITION BY ORDER_NO ORDER BY
NOT_BEFORE_DATE ASC) = 1
THEN 'initial'
ELSE 'other'
END) AS VERSION
FROM FDT_MAPTOOL
WHERE ITEM IN (1032711)
;
Either rank() OR dense_rank() should work here instead of row_number()
nb: note sure if you really need "select distinct"

How to join a table to itself using rank multiple times?

I apologize if there is an existing solution, I tried searching quite a bit but couldnt find much.
Here's what I am trying to do:
I have a table that resembles the first image. I want it to look like the second image. Basically every time rnk_address = 1, I want that row's order_date to be moved into a separate column for that associated user_id.
I'm ultimately trying to calculate the average difference in order_date between all rnk_address = 1, by user_id.
I added a user_counter column using rank because I imagine I will need to do some time of join where i do user_rank = user_rank + 1? But not sure....
Original table:
End result:
You can use conditional aggregation:
select user_id, first_name,
max(case when seqnum = 1 then order_date end) as order_date_1,
max(case when seqnum = 2 then order_date end) as order_date_2,
max(case when seqnum = 3 then order_date end) as order_date_3
from (select t.*,
row_number() over (partition by user_id order by order_date) as seqnum
from t
where rnk_address = 1
) t
group by user_id, first_name;

SQL Teradata - Adding Column of first row data and so on w/ restrictions

Goal: Take the current row of the table and find the most recent item of that customer and append that item data in the next column. If the most recent item is the item 1 of that row then it should proceed to the next row. The logic should always go the the very first row of that customer. Order is based on Transaction Date and partitioned by customer ID. Example of base data provided below in addition to what the desired result set should look like after running the script.
Restrictions: I need this script to work in a CRM tool. Specifically Aprimo/CIM by teradata. With that being said I cannot use CTEs (With) or volatile tables. I also do not want to create multiple joins into itself because of the table volume.
What I have done Thus Far: I feel that the best approach may be to utilize the Window Function, but finding myself creating a lot of nested case statements. I really wanted to know if there was a better way to get the answer i am looking for. I might have to just suck it up and do a join into itself.
Base table
Desired Result Set
Quick Start Base Table:
CREATE VOLATILE TABLE Base
(
customer_id int
, trans_date date
, item varchar(1)
) ON COMMIT PRESERVE ROWS;
Insert Into Base (123,'2017-01-01','A');
Insert Into Base (123,'2017-01-02','B');
Insert Into Base (123,'2017-01-03','C');
Insert Into Base (123,'2017-01-04','D');
Insert Into Base (123,'2017-01-05','E');
Insert Into Base (999,'2017-01-06','F');
Insert Into Base (999,'2017-01-07','G');
Insert Into Base (999,'2017-01-08','H');
There's some CASEs, but not lots :-)
SELECT
dt.*,
CASE
WHEN rn = 1
THEN Min(CASE WHEN rn = 2 THEN item end) Over (PARTITION BY customer_id)
ELSE Min(CASE WHEN rn = 1 THEN item end) Over (PARTITION BY customer_id)
END,
CASE
WHEN rn <=2
THEN Min(CASE WHEN rn = 3 THEN item end) Over (PARTITION BY customer_id)
ELSE Min(CASE WHEN rn = 2 THEN item end) Over (PARTITION BY customer_id)
END,
CASE
WHEN rn <= 3
THEN Min(CASE WHEN rn = 4 THEN item end) Over (PARTITION BY customer_id)
ELSE Min(CASE WHEN rn = 3 THEN item end) Over (PARTITION BY customer_id)
END
FROM
(
SELECT
Row_Number() Over (PARTITION BY customer_id ORDER BY trans_date) AS rn
,base.*
FROM Base
) AS dt
ORDER BY customer_id, trans_date

SQL query which puts columns from rows into one row

I have two tables:
Customer(cust_id, cust_name)
Refund (ref_id, cust_id, ref_date, ref_amount)
cust_id is the foreign key which points out to a Customer.
Each Customer can have several refunds. I want to get a list of customers and only two dates of refunds for each cutomer. Each customer and dates of his refunds must be in one row. I.e. I want to get the foolowing result:
(cust_name, ref_date1, ref_amount1, ref_date2, ref_amount2)
For example - 'John Smith', '06/06/2012', 500.0, '08/05/2014', 345.5
How can I get this?
Thanx!
(If it is important I use Oracle 11g)
The following gets the two most recent refunds:
select c.cust_name,
max(case when seqnum = 1 then ref_date end) as ref_date1,
max(case when seqnum = 1 then ref_amount end) as ref_amount1,
max(case when seqnum = 2 then ref_date end) as ref_date2,
max(case when seqnum = 2 then ref_amount end) as ref_amount2
from customer c join
(select r.*,
row_number() over (partition by cust_id order by ref_date desc) as seqnum
from refund r
) r
on c.cust_id = r.cust_id
where seqnum <= 2
group by c.cust_name;