Finding first transaction after death of date - sql

Could anyone please help to enhance the query below,
select
columns
from
(
select t.*,
sum (case when TRAN_DATE >= '20170701' then 1 end)
over (partition by acct_no order by TRAN_DATE, TRAN_TIME ) as sm_i
from (
Select
Columns
FROM
#BASEtable DTRAN
INNER JOIN sometable
where condition) t) t
where sm_i = 1
order by acc_no
here is the data example, (attached)
Company Acct_no Tran_Date Death_of_date
1 123 20170725 20170702
1 123 20170825 20170702
1 123 20170925 20170702
2 456 20191025 20200101
2 456 20191125 20200101
2 456 20191225 20200101
Result expected: Row no 1 , as that is the first transaction for that account after the death_of_date
I am sorting the data based on 20170701, that is it will pick the first transaction happened after this date should be picked up which is working with the above query.
Now, i want to set the value of '20170701' with the dynamic value , i.e. need the first transaction of every account after its death of date..
I replaced the partition code the below code,
sum(case when tran_Date > = (select death_of_date from #basetable a where a.acct_no = t.acct_no ) then 1 end)
over partition by acct_no order by tran_Date , tran_Time) as sm_i
but getting error saying, subquery retuned more than one result which is not application where using > , = and so on.
Please help to enhance this code in sql server. Appreciate your help in advance!
enter image description here

Assuming two things:
You have data with the four columns you have specified.
For each account, you want the first row meeting your date condition.
Then you can use window functions and filtering:
select t.*
from (select t.*,
row_number() over (partition by Company, Acct_no order by Tran_Date) as seqnum
from t
where tran_date > death_of_date
) t
where seqnum = 1;

Related

Oracle SQL: check amount of active users on given date (check closest date of grouped field)

Have a table given, holding the status history of a user:
ID
USERID
MODIFIED
STATUS
1
1
01.01.2020
inactive
2
1
01.07.2020
active
3
2
04.08.2020
active
4
2
04.06.2020
active
5
2
01.08.2020
inactive
6
2
01.10.2020
active
7
3
01.09.2020
inactive
I want to provide a date, i.e. 01.07.2020, and understand how many UserIds were active on that day.
I therefor need to check the modified date which is closest but not above 01.07.2020, grouped by the userid.
Desired result for 01.07.2020:
ID
USERID
MODIFIED
STATUS
2
1
01.07.2020
active
4
2
04.06.2020
active
From there I could just sum the status, and see I had two users active on the checked date of 01.07.2020.
Current approach for first step:
select max(id), userid, max(modified)
keep (dense_rank first order by modified) as id
from MY_TABLE
where modified <= '01.07.2020'
group by userid;
it does not yet provide fully correct results
the final step would then be a simple sum I assume, something like:
Select sum(case when status = 'active' then 1 else 0 end) as "active_users"
from MY_TABLE t1
inner join (
select max(id)
keep (dense_rank first order by modified) as id
from MY_TABLE
where modified <= '01.07.2020'
group by userid
) t2 on t1.id = t2.id
You can use row_number() to get the last status as of that date:
select count(*)
from (select t.*,
row_number() over (partition by userid order by modified desc) as seqnum
from my_table t
where t.modified <= date '2020-07-01'
) t
where seqnum = 1 and status = 'Active';
Another option is a correlated subquery:
select count(*)
from my_table t
where t.modified = (select max(t2.modified)
from my_table t2
where t2.userid = t.userid and
t2.modified <= date '2020-07-01'
) and
t.status = 'Active';
Or, you can use two levels of aggregation:
select count(*)
from (select userid,
max(status) keep (dense_rank first order by modified desc) as status
from my_table t
where t.modified <= date '2020-07-01'
group by userid
) t
where status = 'Active';
Since you are looking for a count of active users nearest to the supplied date, the following would work.
select count(distinct userid)
from table
where modified <= '01.07.2020'
and status='Active'

GET SUM of particular column along with value of another column in last row

I am newbie to Sql, I want to get SUM of a particular column from a table and also the value of another Column from the table which is in the last row of column used in SUM.
For Eg:
Below is my Table I want sum of all amount fields where Code is 1 and and also a qty field which is at the last occurrence of code with value 1 in table
Table Image
I want some thing like below
select SUM(amount) from table where Code = 1 UNION ALL Select qty from test where Code = 1 and id = MAX(id) for/where code = 1 ;
If I get you correctly your need something like following, here is the demo.
select
code,
total_amount,
qty
from
(
select
code,
sum(amount) over (order by id) as total_amount,
qty,
row_number() over (partition by code order by id desc) as rnk
from yourTable
where code = 1
) val
where rnk =1
Output:
*-----------------------*
|code total_amount qty |
*-----------------------*
| 1 80 20 |
*-----------------------*
You can do this without a subquery, if you want:
select distinct top (1) sum(amount) over () as amount, qty
from t
where code = 1
order by id desc;

Calculate lead-time between selected rows (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

How To Set Row As Null After Meeting A Certain Criteria

I am trying to have "Cumulative Customers" be NULL after the first 5 "Cumulative Customers":
SUM(Customer) OVER (PARTITION BY Product ORDER BY date DESC) cumulative_customers
The final output will look like this:
Use a CASE expression:
SELECT CASE WHEN cumulative_customers < 5
OR cumulative_customers = 5 AND customer >= 1
THEN cumulative_customers END AS cumulative_customers
, ... -- more columns
FROM (
SELECT ... -- your current query here
) sub;
If the ELSE part is missing it defaults to NULL. You can spell that out, too, if you prefer.
I use customer >= 1 just in case there can be values greater than 1 (unlike your demo suggests).
If you don't want a subquery, you can do this using case:
select (case when SUM(Customer) OVER (PARTITION BY Product ORDER BY date DESC) <= 5
then SUM(Customer) OVER (PARTITION BY Product ORDER BY date DESC)
end) as cumulative_customers
Erwin's solution also works if you want a subquery or CTE.

Join two queries from the same table - SELECT DISTINCT?

I have two tables linked by an AUTO_KEY field, from one table I'm retrieving the number (id), from the other I get several statuses by number(id), each status has a date associated to it.
I need to restrict the results only to the maximum/latest date for all numbers(ids) and the corresponding status
SELECT
OPERATION.NUMBER,
STATUS.STATUS,
Max(STATUS.DATE)
FROM
STATUS,
OPERATION
WHERE
OPERATION.AUTO_KEY = STATUS.AUTO_KEY
From here
Number Status Date
-----------------------------
1 A 10/20/13
1 B 10/15/13
2 A 10/10/13
2 AX 10/05/13
2 AD 10/03/13
3 DD 10/03/13
The outcome should be
Number Status Date
-----------------------------
1 A 10/20/13
2 A 10/10/13
3 DD 10/03/13
Thanks in advance
You can use a CTE with ROW_NUMBER() function. Also Please use a Table JOIN instead FROM STATUS, OPERATION
;With CTE AS (
SELECT O.NUMBER, S.STATUS, S.DATE,
ROW_NUMBER() OVER (ORDER BY S.DATE DESC) RN
FROM STATUS S JOIN OPERATION O
ON O.AUTO_KEY = S.AUTO_KEY
)
SELECT NUMBER, STATUS, DATE
FROM CTE
WHERE RN = 1
ORDER BY NUMBER
SELECT OPERATION.CNUMBER,
STATUS.STATUS,
STATUS.CDATE
FROM STATUS,
OPERATION
WHERE OPERATION.AUTO_KEY = STATUS.AUTO_KEY
AND STATUS.CDATE = (
SELECT MAX(STATUS.CDATE) MAX_DATE
FROM STATUS,
OPERATION
WHERE OPERATION.AUTO_KEY = STATUS.AUTO_KEY
GROUP BY OPERATION.CNUMBER )