I have a table of customers that join and leave a company
ID ActiveFrom ActiveTo
I have for example a where clause that has
where ActiveFrom <= '20170101'
and Activeto < '20170201'
but some times a customer has decided to re-join the company a few days later because of a deal.
example customer:
ID ActiveFrom ActiveTo
1 2000-01-01 2017-01-03
1 2017-01-28 Null
2 2000-01-01 2017-01-06
I want to exclude the customers that do this, from the customers that leave the company but come back
so I want id 2 to be returned only
please help
You can try this.
SELECT * FROM Customer C1
WHERE C1.ActiveFrom <= '20170101'
AND C1.Activeto < '20170201'
AND NOT EXISTS ( SELECT * FROM Customer C2
WHERE C1.ID = C2.ID
AND C2.ActiveTo IS NULL )
You could use a subquery on WHERE statement:
SELECT *
FROM tablename
WHERE (SELECT id FROM tablename WHERE activeTo is null) <> id
GROUP By id
You can find more information here:
https://www.techonthenet.com/sql_server/subqueries.php
Related
I have a hard struggle with this problem.
I have the following table:
TicketNumber
OpenTicketDate YYYY_MM
ClosedTicketDate YYYY_MM
1
2018-1
2020-1
2
2018-2
2021-2
3
2019-1
2020-6
4
2020-7
2021-1
I would like to create an extra column which would monitor the open tickets at the given OpenTicketDate.
So the new table would look like this:
TicketNumber
OpenTicketDate YYYY_MM
ClosedTicketDate YYYY_MM
OpenTicketsLookingBackwards
1
2018-1
2020-1
1
2
2018-2
2021-2
2
3
2019-1
2020-6
3
4
2020-7
2021-1
2
The logic behind the 4th (extra) column is that it looks at the previous records & current record where the ClosedTicketsDate > OpenTicketDate.
For example ticketNumber 4 has '2' open tickets because there are only 2 ClosedTicketDate records where ClosedTicketDate > OpenTicketDate.
The new column only fills data based on looking at prevoius records. It is backward looking not forward.
Is there anyone who can help me out?
You could perform a self join and aggregate as the following:
Select T.TicketNumber, T.OpenTicketDate, T.ClosedTicketDate,
Count(*) as OpenTicketsLookingBackwards
From table_name T Left Join table_name D
On Cast(concat(T.OpenTicketDate,'-1') as Date) < Cast(concat(D.ClosedTicketDate,'-1') as Date)
And T.ticketnumber >= D.ticketnumber
Group By T.TicketNumber, T.OpenTicketDate, T.ClosedTicketDate
Order By T.TicketNumber
You may also try with a scalar subquery as the following:
Select T.TicketNumber, T.OpenTicketDate, T.ClosedTicketDate,
(
Select Count(*) From table_name D
Where Cast(concat(T.OpenTicketDate,'-1') as Date) <
Cast(concat(D.ClosedTicketDate,'-1') as Date)
And T.ticketnumber >= D.ticketnumber
) As OpenTicketsLookingBackwards
From table_name T
Order By T.TicketNumber
Mostly, joins tend to outperform subqueries.
See a demo.
I have data related material transactions in one table and log history header data related to materials is in another table and detailed log history data in third table. I'm trying to get different status update dates matched to material table but I get duplicate rows for one material transaction
Original material transaction table:
ORDER_NO
MATERIAL
QTY
0001
MAT01
2
0002
MAT02
5
Original Log History Header transaction table:
ORDER_NO
LOG_ID
0001
1001
0001
1002
Status code 1 refers to Opened and code 2 to Closed
Detailed Log History table:
LOG_ID
STATUS_CODE
DATE
1001
1
11/12/2021
1002
2
15/12/2021
With following SQL query:
SELECT
TO_CHAR (m.order_no) order_no,
m.material,
a.date opened_date,
ab.closed_date
FROM MATERIAL_TRANSACTIONS m
INNER JOIN HISTORY_LOG t
ON m.ORDER_NO = t.ORDER_NO
INNER JOIN HISTORY_LOG_DETAILED a
ON t.LOG_ID = a.LOG_ID
AND a.STATUS_CODE = '1'
INNER JOIN HISTORY_LOG_DETAILED ab
ON t.LOG_ID = ab.LOG_ID
AND ab.STATUS_CODE = '2'
I get following result:
ORDER_NO
MATERIAL
QTY
OPENED_DATE
CLOSED_DATE
0001
MAT01
2
11/12/2021
0001
MAT01
2
15/12/2021
And I would like to get the status dates to the same row as below:
ORDER_NO
MATERIAL
QTY
OPENED_DATE
CLOSED_DATE
0001
MAT01
2
11/12/2021
15/12/2021
I would appreciate all the help I can get and am very sorry if there already is topic for similar issue.
Your problem occurs because you join the history table, which holds 2 records for the order. You could flatten this if you use 2 inline tables that hold exactly 1 record.
with opened_dates as (
select h.order_id, d.date
from history h
inner join details d on h.log_id = d.log_id and d.status_code = '1'
), closed_dates as (
select h.order_id, d.date
from history h
inner join details d on h.log_id = d.log_id and d.status_code = '2'
)
select to_char (m.order_no) order_no,
m.material,
o.date opened_date,
c.date closed_date
from material_transactions m
join opened_dates o on m.order_no = o.order_no
join closed_dates c on m.order_no = c.order_no
;
Just an idea :
I joined HISTORY_LOG and HISTORY_LOG_DETAILED tables to get dates for specific status, and set as OPENED_DATE and CLOSED_DATE (if status 1 , then opened date is DATE column, otherwise set it as 01.01.0001)
After that grouped those records by ORDER_NO and maxed the date values to get actual OPENED_DATE and CLOSED_DATE .
Finally joined this subquery with MATERIAL_TRANSACTIONS table :
SELECT
TO_CHAR (M.ORDER_NO) ORDER_NO,
M.MATERIAL,
QTY,
L_T.OPENED_DATE,
L_T.CLOSED_DATE
FROM MATERIAL_TRANSACTIONS M
INNER JOIN
(
SELECT L.ORDER_NO ,
MAX( CASE WHEN LD.STATUS_CODE = 1 THEN LD.DATE ELSE TO_DATE('01.01.0001','dd.mm.yyyy') END ) OPENED_DATE
MAX( CASE WHEN LD.STATUS_CODE = 2 THEN LD.DATE ELSE TO_DATE('01.01.0001','dd.mm.yyyy') END ) CLOSED_DATE
FROM
HISTORY_LOG L
INNER JOIN HISTORY_LOG_DETAILED LD ON LD.LOG_ID = L.LOG_ID
GROUP BY L.ORDER_NO
) L_T on L_T.ORDER_NO = M.ORDER_NO
Note: I didnt test it. So there can be small syntax errors. Please check it and for better help add a fiddle so i can test my query
I have a dataset as shown below in a booking table
Custid ApptDate oldornewApp
-------------------------------
100 01-jan-2013 old
100 01-jan-2014 old
100 01-oct-2016 new
100 12-oct-2016 new
200 01-feb-2015 old
200 10-oct-2016 new
I am trying for an output which shows max date based on the oldornewapp column for each customer
custid MaxApptDateOldApp MaxapptDatenewapp
---------------------------------------------
100 01-jan-2014 12-oct-2016
200 01-feb-2015 10-oct-2016
Please let me know a best way to get the resultset, I am using SQL Server 2012
Thanks in advance
SELECT
Custid
,MAX(CASE WHEN oldornewApp = 'old' THEN ApptDate END) as MaxApptDateOldApp
,MAX(CASE WHEN oldornewApp = 'new' THEN ApptDate END) as MaxApptDateNewApp
FROM
TableName
GROUP BY
Custid
You can use conditional aggregation to get to your desired result.
You can use join and group by
select old.Custid, max(old.ApptDate) as MaxApptDateOldApp, max(new.ApptDate) as MaxApptDateNewApp
from my_table as old
where a.oldornewApp = 'old';
group by a,Custid
inner join (
select b.Custid, max(b.ApptDate) as AppDate
from my_table as b
where b.oldornewApp = 'new';
group by b.Custid
) new
Let's say I have this table with the following data:
Service_ID Cust_ID Service_Date Next_Service_Date
-----------------------------------------------------
1 15 2016-01-1 2016-01-31
2 21 2016-01-1 2016-01-31
3 15 2016-01-31 2016-03-1
I need a condition to check if Next_Service_Date is found in Service_Date for each customer not the whole table.
For example customer with id = 15 and Service_ID = 3, you can see Service_Date was made on 2016-01-31
Same as the Next_Service_Date with Service_ID = 1
So output of the query should be
Service_ID Cust_ID Service_Date Next_Service_Date
------------------------------------------------------
2 21 2016-01-1 2016-01-31
I hope I made everything clear.
Note why I want to show record # 2 because that customer has no records in Service_Date that matches the date in Next_Service_Date
If I understand correctly, you want not exists:
select bt.*
from belowtable bt
where not exists (select 1
from belowtable bt2
where bt2.cust_id = bt.cust_id and
(bt2.next_service_date = bt.service_date or
bt2.service_date = bt.next_serice_date
)
);
Edited -- I think I understand your question now -- you want to find all records that should have a next service date scheduled but do not? If so, a combination semi-join and anti-join would do it.
If I'm off the mark, please let me know where I misunderstood.
select
t1.*
from
MyTable t1
where exists (
select null
from MyTable t2
where
t1.Next_Service_Date = t2.Service_Date
)
and not exists (
select null
from MyTable t2
where
t1.Cust_Id = t2.Cust_Id and
t1.Next_Service_Date = t2.Service_Date
)
Given a table
custid | date | action
1 | 2011-04-01 | activate
1 | 2011-04-10 | deactivate
1 | 2011-05-02 | activate
2 | 2011-04-01 | activate
3 | 2011-03-01 | activate
3 | 2011-04-01 | deactivate
The database is PostgreSQL.
I want an SQL query to show customers that were active at any stage during May.
So, in the above, that would be 1 and 2.
I just can't get my head around the way to approach this. Any pointers?
update
Customer 2 was active during May, as he was activated Before May, and not Deactivated since he was Activated. As in, I'm alive this Month, but wasn't born this month, and I've not died.
select distinct custid
from MyTable
where action = 'active' and date >= '20110501' and date < '20110601'
This approach won't work, as it only shows activations during may, not 'actives'.
Note: This would be a starting point and only works for 2011.
Ignoring any lingering bugs, this code (for each customer) looks at 1) The customer's latest status update before may and 2) Did the customer become active during may?
SELECT
Distinct CustId
FROM
MyTable -- Start with the Main table
-- So, was this customer active at the start of may?
LEFT JOIN -- Find this customer's latest entry before May of This Year
(select
max(Date)
from
MyTable
where
Date < '2011-05-01') as CustMaxDate_PreMay on CustMaxDate_PreMay.CustID = MyTable.CustID
-- Return a record "1" here if the Customer was Active on this Date
LEFT JOIN
(select
1 as Bool,
date
from
MyTable
) as CustPreMay_Activated on CustPreMay_Activated.Date = CustMaxDate_PreMay.Date and CustPreMay_Activated.CustID = MyTable.CustID and CustPreMay_Activated = 'activated'
-- Fallback plan: If the user wasn't already active at the start of may, did they turn active during may? If so, return a record here "1"
LEFT JOIN
(select
1 as Bool
from
MyTable
where
Date <= '2011-05-01' and Date < '2011-06-01' and action = 'activated') as TurnedActiveInMay on TurnedActiveInMay .CustID = MyTable.CustID
-- The Magic: If CustPreMay_Activated is Null, then they were not active before May
-- If TurnedActiveInMay is also Null, they did not turn active in May either
WHERE
ISNULL(CustPreMay_Activated.Bool, ISNULL(TurnedActiveInMay.Bool, 0)) = 1
Note:
You might need replace the `FROM MyTable' with
From (Select distinct CustID from MyTable) as Customers
It is unclear to me just looking at this code whether or not it will A) be too slow or B) somehow cause dupes or problems due starting the FROM clause # MYTable which may contain many records per customer. The DISTINCT clause probably takes care of this, but figured I'd mention this workaround.
Finally, I'll leave it to you to make this work across different years.
Try this
select t2.custid from
(
-- select the most recent entry for each customer
select custid, date, action
from cust_table t1
where date = (select max(date)
from cust_table where custid = t1.custid)
) as t2
where t2.date < '2011-06-01'
-- where the most recent entry is in May or is an activate entry
-- assumes they have to have an activate entry before they get a deactivate entry
and (date > '2011-05-01' or [action] = 'activate')
In PostgreSQL 8.4+:
WITH ActivateDates AS (
SELECT
custid,
date,
ROW_NUMBER() OVER (PARTITION BY custid ORDER BY date) AS rownum
FROM atable
WHERE action = 'activate'
),
DeactivateDates AS (
SELECT
custid,
date,
ROW_NUMBER() OVER (PARTITION BY custid ORDER BY date) AS rownum
FROM atable
WHERE action = 'deactivate'
),
ActiveRanges AS (
SELECT
a.custid,
a.date AS activated,
COALESCE(b.date, '21000101'::date) AS deactivated
FROM ActivateDates a
LEFT JOIN DeactivateDates d ON a.custid = d.custid AND a.rownum = d.rownum
)
SELECT DISTINCT custid
FROM ActiveRanges
WHERE deactivated > '20110501'
AND activated < '20110601'