get status on specific date - sql

Table structure: (database is oracle 12c)
CUSTOMER_ID | STATUS | STATUS_FROM_DATE
101 | ABC | 10-01-2015
101 | PQR | 27-02-2015
101 | LMN | 04-08-2015
101 | ABC | 08-09-2015
Question: How to get status of customer for specific date from above table?
For example:
CUSTOMER_ID | Input Date | Expected Output
101 | 15-01-2015 | ABC
101 | 27-02-2015 | PQR
101 | 28-02-2015 | PQR
101 | 10-09-2015 | ABC
In above example,
ABC was the status of customer on 15-01-2015, because this is set on 10-01-2015 and not changed till 27-02-2015.
PQR was the status of customer on 28-02-2015, because this is set on 27-02-2015 and not changed till 04-08-2015.

You can use lead analytic function to get the end of interval. Then just search using between.
select * from
(
select
customer_id,
status,
status_from_date,
nvl(lead(status_from_date) over (partition by customer_id order by status_from_date)-1,
to_date('2099','yyyy')
)as end_date
from your_table
)
where your_date_here between status_start_date and end_date

Using newly introduced row limiting clause in Oracle 12c,
select <your input date> as date_, expected_status
from myt
where status_date <= <your input date>
order by status_date desc
fetch first 1 rows only;

SQL FIDDLE DEMO
with ranges as (
select t.*,
lead(STATUS_FROM_DATE,1, (select sysdate from dual))
over (partition by CUSTOMER_ID order by STATUS_FROM_DATE) as status_change
from Table1 t
)
select r.status, s."Date", s."Expected Output"
from ranges r
inner join TestStatus s
on s."Date" < r.status_change
and s."Date" >= r.STATUS_FROM_DATE;

Related

Finding created on dates for duplicates in SQL

I have one table of contact records and I'm trying to get the count of duplicate records that were created on each date. I'm not looking to include the original instance in the count. I'm using SQL Server.
Here's an example table
| email | created_on |
| ------------- | ---------- |
| aaa#email.com | 08-16-22 |
| bbb#email.com | 08-16-22 |
| zzz#email.com | 08-16-22 |
| bbb#email.com | 07-12-22 |
| aaa#email.com | 07-12-22 |
| zzz#email.com | 06-08-22 |
| aaa#email.com | 06-08-22 |
| bbb#email.com | 04-21-22 |
And I'm expecting to return
| created_on | dupe_count |
| ---------- | ---------- |
| 08-16-22 | 3 |
| 07-12-22 | 2 |
| 06-08-22 | 0 |
| 04-21-22 | 0 |
Edited to add error message:
error message
I created a sub table based on email and created date row number. Then, you query that, and ignore the date when the email first was created (row number 1). Works perfectly fine in this case.
Entire code:
Create table #Temp
(
email varchar(50),
dateCreated date
)
insert into #Temp
(email, dateCreated) values
('aaa#email.com', '08-16-22'),
('bbb#email.com', '08-16-22'),
('zzz#email.com', '08-16-22'),
('bbb#email.com', '07-12-22'),
('aaa#email.com', '07-12-22'),
('zzz#email.com', '06-08-22'),
('aaa#email.com', '06-08-22'),
('bbb#email.com', '04-21-22')
select datecreated, sum(case when r = 1 then 0 else 1 end) as duplicates
from
(
Select email, datecreated, ROW_NUMBER() over(partition by email
order by datecreated) as r from #Temp
) b
group by dateCreated
drop table #Temp
Output:
datecreated duplicates
2022-04-21 0
2022-06-08 0
2022-07-12 2
2022-08-16 3
You can calculate the difference between total count of emails for every day and the count of unique emails for the day:
select created_on,
count(email) - count(distinct email) as dupe_count
from cte
group by created_on
It seems I have misunderstood your request, and you wanted to consider previous created_on dates' too:
ct as (
select created_on,
(select case when (select count(*)
from cte t2
where t1.email = t2.email and t1.created_on > t2.created_on
) > 0 then email end) as c
from cte t1)
select created_on,
count(distinct c) as dupe_count
from ct
group by created_on
order by 1
It seems that in oracle it is also possible to aggregate it using one query:
select created_on,
count(distinct case when (select count(*)
from cte t2
where t1.email = t2.email and t1.created_on > t2.created_on
) > 0 then email end) as c
from cte t1
group by created_on
order by 1

How to Order by in Nested Query?

I have a table like this :
MY_TABLE
Id Name Brand Date
-- ---- ----- ------
1 ABC 1 18-09-2019
2 XYZ 1 19-09-2019
3 MNO 1 18-09-2019
4 ABC 1 19-09-2019
5 PQR 2 17-06-2020
6 MNO 1 19-03-2019
7 ABC 2 19-09-2019
I want to write a query in such a way that first i need to sort based on Date. Based on that result i have to sort count(Name) for a brand.Like this
ID Name Count(Name) Brand date
--- ---- ---------- ------ -----
1 ABC 2 1 19-09-2019
2 XYZ 1 1 19-09-2019 // Eventhough count is less it came second because 19-09-2019 is latest than 19-03-2019
3 MNO 2 1 18-09-2019
You can group by name and aggregate:
select
name,
count(*) counter,
max(brand) brand,
max(trunc(datee)) maxdate
from my_table
where brand = 1
group by name
order by maxdate desc, counter desc
I used trunc(datee) because your dates (as we found out) contain a time part.
See the demo.
Results:
> NAME | COUNTER | BRAND | MAXDATE
> :--- | ------: | ----: | :--------
> ABC | 2 | 1 | 19-SEP-19
> XYZ | 1 | 1 | 19-SEP-19
> MNO | 2 | 1 | 18-SEP-19
You have to first create the count using a group by and then sort by using order by which supports multiple fields (comma separated). And once you give an alias to the column with the count you an use this alias in the order by:
select name, brand, min(date), count(name) as NumRecords, min(id) as id
from my_table
group by name, brand
order by date, NumRecords
I added the min(id) because it seems that you are trying to show the minimum id.
I think you want to select one row per name/brand pair based on the highest count on the most recent date:
select t.*
from (select t.*,
row_number() over (partition by name, brand order by date desc, count desc) as seqnum
from t
) t
where seqnum = 1;
SELECT name, COUNT(*) BrandCount, MAX(brand) brand, date from my_table
ORDER BY date
You may use [ ASC | DESC ] in the end depending upon requirement.
Similar query.
Hope it sorts
Is this what you want?
SELECT MIN(ID), Name,
Count(Name), Brand, MIN(date) as
Date_MIN
From Table where Brand =1 Group by
Brand,Name
Order by Date_MIN desc,Count(Name)
Desc;
;

SQL order by two column, omit if second column doesn't meet the order

Let's say we have next data
id | date | price
------------------------
1 | 10-09-2016 | 200
2 | 11-09-2016 | 190
3 | 12-09-2016 | 210
4 | 13-09-2016 | 220
5 | 14-09-2016 | 200
6 | 15-09-2016 | 200
7 | 16-09-2016 | 230
8 | 17-09-2016 | 240
and we have to order by date first, and price second, however if the price must be in order. If current price is less than previous we should omit this row, and the result will be:
id | date | price
------------------------
1 | 10-09-2016 | 200
3 | 12-09-2016 | 210
4 | 13-09-2016 | 220
7 | 16-09-2016 | 230
8 | 17-09-2016 | 240
Is it possible without join?
Use LAG window function
SELECT *
FROM (SELECT *,
Lag(price)OVER( ORDER BY date) AS prev_price
FROM Yourtable) a
WHERE price > prev_price
OR prev_price IS NULL -- to get the first record
If "previous" is supposed to mean the previous row in the output, then keep track of a running maximum. Postgres solution with a window function in a subquery:
SELECT id, date, price
FROM (
SELECT *, price >= max(price) OVER (ORDER BY date, price) AS ok
FROM tbl
) sub
WHERE ok;
If Postgres:
select id, date, price
from
(select
t.*,
price - lag(price, 1, price) over (order by id) diff
from
your_table) t
where diff > 0;
If MySQL:
select id, date, price from
(
select t.*,
price - #lastprice diff,
#lastprice := price
from
(select *
from your_table
order by id) t
cross join (select #lastprice := 0) t2
) t where t.diff > 0;

Remove duplicate rows query result except for one in Microsoft SQL Server?

How would I delete all duplicate month from a Microsoft SQL Server Table?
For example, with the following syntax I just created:
SELECT * FROM Cash WHERE Id = '2' AND TransactionDate between '2014/07/01' AND '2015/02/28'
and the query result is:
+----+-------------------------+
|Id | TransactionDate |
+----+-------------------------+
| 2 | 2014-07-22 00:00:00.000 |
| 2 | 2014-08-09 00:00:00.000 |
| 2 | 2014-08-25 00:00:00.000 |
| 2 | 2014-08-29 00:00:00.000 |
| 2 | 2015-01-27 00:00:00.000 |
| 2 | 2015-01-28 00:00:00.000 |
+----+-------------------------+
How would I remove duplicates month which is only return any 1 value for any 1 month each, like this result:
+----+-------------------------+
|Id | TransactionDate |
+----+-------------------------+
| 2 | 2014-07-22 00:00:00.000 |
| 2 | 2014-08-09 00:00:00.000 |
| 2 | 2015-01-27 00:00:00.000 |
+----+-------------------------+
You can do it with the help of ROW_NUMBER.
This will tell you which are the rows you are going to keep
SELECT id,transactionDate, ROW_NUMBER() OVER ( PARTITION BY YEAR(TransactionDate ),MONTH(TransactionDate ) ORDER BY TransactionDate ) firstTrans
FROM Cash
WHERE Id = '2' AND
TransactionDate between '2014/07/01' AND '2015/02/28'
You can delete the other rows with a CTE.
with myCTE (id,transactionDate, firstTrans) AS (
SELECT id,transactionDate, ROW_NUMBER() OVER ( PARTITION BY YEAR(TransactionDate ),MONTH(TransactionDate ) ORDER BY TransactionDate ) firstTrans
FROM Cash
WHERE Id = '2' AND
TransactionDate between '2014/07/01' AND '2015/02/28'
)
delete from myCTE where firstTrans <> 1
Will only keep one transaction for each month of each year.
EDIT:
filter by the row_number and will only return the rows you want
select id, transactionDate from (SELECT id,transactionDate, ROW_NUMBER() OVER ( PARTITION BY YEAR(TransactionDate ),MONTH(TransactionDate ) ORDER BY TransactionDate ) firstTrans
FROM Cash
WHERE Id = '2' AND
TransactionDate between '2014/07/01' AND '2015/02/28') where firstTrans = 1
When you run this query you will get the highest Id for each month in each year.
SELECT MAX(<IdColumn>) AS Id, YEAR(<DateColumn>) AS YE, MONTH(<DateColumn>) AS MO FROM <YourTable>
GROUP BY YEAR(<DateColumn>), MONTH(<DateColumn>)
If needed, for example, you can late delete rows that their Id is not in this query.
Select only the first row per month
SELECT *
FROM Cash c
WHERE c.Id = '2'
AND c.TransactionDate between '2014/07/01' AND '2015/02/28'
AND NOT EXISTS ( SELECT 'a'
FROM Cash c2
WHERE c2.Id = c.Id
AND YEAR(c2.TransactionDate) * 100 + MONTH(c2.TransactionDate) = YEAR(c.TransactionDate) * 100 + MONTH(c.TransactionDate)
AND c2.TransactionDate < c.TransactionDate
)

SQL Server : select from duplicate columns where date newest

I have inherited a SQL Server table in the (abbreviated) form of (includes sample data set):
| SID | name | Invite_Date |
|-----|-------|-------------|
| 101 | foo | 2013-01-06 |
| 102 | bar | 2013-04-04 |
| 101 | fubar | 2013-03-06 |
I need to select all SID's and the Invite_date, but if there is a duplicate SID, then just get the latest entry (by date).
So the results from the above would look like:
101 | fubar | 2013-03-06
102 | bar | 2013-04-04
Any ideas please.
N.B the Invite_date column has been declared as a nvarchar, so to get it in a date format I am using CONVERT(DATE, Invite_date)
You can use a ranking function like ROW_NUMBER or DENSE_RANK in a CTE:
WITH CTE AS
(
SELECT SID, name, Invite_Date,
rn = Row_Number() OVER (PARTITION By SID
Order By Invite_Date DESC)
FROM dbo.TableName
)
SELECT SID, name, Invite_Date
FROM CTE
WHERE RN = 1
Demo
Use Row_Number if you want exactly one row per group and Dense_Rank if you want all last Invite_Date rows for each group in case of repeating max-Invite_Dates.
select t1.*
from your_table t1
inner join
(
select sid, max(CONVERT(DATE, Invite_date)) mdate
from your_table
group by sid
) t2 on t1.sid = t2.sid and CONVERT(DATE, t1.Invite_date) = t2.mdate
select
SID,name,MAX(Invite_date)
FROM
Table1
GROUP BY
SID
http://sqlfiddle.com/#!2/6b6f66/1