Changing multiple select query into CTE query - sql

I want to change this particular simple select query into CTE in order to optimize it, as this simple three select query taking 4 mins and so, I want to reduce the execution time of it by converting it into CTE query.
select sum(AMOUNT) usd from data where status IN ('ACCEPTED','REJECTED','CANCELLED') and date between '01-SEP-18' and '30-NOV-18'
UNION
select sum(AMOUNT) usd from data where status IN ('ACCEPTED','REJECTED','CANCELLED') and date between '01-NOV-18' and '31-JAN-19'
UNION
select sum(AMOUNT) usd from data where status IN ('ACCEPTED','REJECTED','CANCELLED') and date between '01-FEB-19' and '30-APR-19'
ORDER BY usd DESC ;
The expected result should be one column showing the result of the three queries, which is the sum as usd of the amount in this query.
Example:
USD
100 (timeline 1)
200 (timeline 2)
300 (timeline 3)
The one I tried:
Can you conver this CTE query with sum instead of count.
WITH cte AS (
SELECT
COUNT(CASE WHEN status_date BETWEEN '2018-11-01' AND '2019-01-31' THEN 1 END) AS cnt1,
COUNT(CASE WHEN status_date BETWEEN '2019-02-01' AND '2019-04-30' THEN 1 END) AS cnt2,
COUNT(CASE WHEN status_date BETWEEN '2019-05-01' AND '2019-07-31' THEN 1 END) AS cnt3
FROM data
WHERE status IN ('ACCEPTED', 'REJECTED', 'CANCELLED')
)
SELECT cnt1 FROM cte
UNION ALL
SELECT cnt2 FROM cte
UNION ALL
SELECT cnt3 FROM cte;

I am guessing that you don't want the timelines to overlap, so:
select (case when date between '01-SEP-18' and '30-NOV-18' then 'timeline1'
when date between '01-DEC-18' and '31-JAN-19' then 'timeline2'
when date between '01-FEB-19' and '30-APR-19' then 'timeline3'
end), sum(AMOUNT) as usd
from data
where status IN ('ACCEPTED','REJECTED','CANCELLED') and
date between '01-SEP-18' and '30-APR-19'
group by (case when date between '01-SEP-18' and '30-NOV-18' then 'timeline1'
when date between '01-DEC-18' and '31-JAN-19' then 'timeline2'
when date between '01-FEB-19' and '30-APR-19' then 'timeline3'
end)
order by usd desc;
If you do, separate columns are probably simpler:
select sum(case when date >= date '2018-09-01' and date < date '2018-12-01' then amount end) as usd_timeline1,
sum(case when date >= date '2018-11-01' and date < date '2019-02-01' then amount end) as usd_timeline2
sum(case when date >= date '2019-02-01' and date '2019-05-01' then amount end) as usd_timeline3
from data
where status IN ('ACCEPTED','REJECTED','CANCELLED') and
date >= date '2018-09-01' and date < date '2019-05-01'
order by usd desc;
Note that this uses date constants using the ISO standard YYYY-MM-DD format and the date keyword.

Related

Suggested way to do a 'parallel period' SQL statement

Let's say I want to get the profit between two dates. Then I can do something like this:
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
I would then like to compare it to a previous period offset by a fixed amount. It could be written something like this to get it in two rows:
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
UNION ALL
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' - INTERVAL 1 YEAR AND '2014-02-01' - INTERVAL 1 YEAR AND <other_filters>
Is there a way to do this without a union? I am looking for something like this:
SELECT
SELECT SUM(Profit),
???
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
I think the tricky part here is how to 'un-do' the where filter for the offseted-time calculation.
You can use conditional aggregation and OR the range checks in the WHERE clause (unless they are subsequent in which case you can combine them directly of course).
SELECT sum(CASE
WHEN date >= '2014-01-01'
AND date < '2014-02-02' THEN
profit
ELSE
0
END),
sum(CASE
WHEN date >= '2014-01-01' - INTERVAL 1 YEAR
AND date < '2014-02-02' - INTERVAL 1 YEAR THEN
profit
ELSE
0
END)
FROM sales
WHERE date >= '2014-01-01'
AND date < '2014-02-02'
OR date >= '2014-01-01' - INTERVAL 1 YEAR
AND date < '2014-02-02' - INTERVAL 1 YEAR;
Note: Prefer not to use BETWEEN here but check for a right half open range check. That way, if the precision of date changes, records on the end past midnight are still in the results.

How to get count of a column between a certain date range without changing the Where clause in SQL

I have a table that has account number, group category and date.
I have a query that does this
Select count(AccNum)
FROM Table
Where date BETWEEN '2020-02-01' AND '2020-03-31'
AND
group IN ('groupA','groupB')
Now is there any way for me to make it work like this
Select count(AccNum) Where date between 2020-02-01 AND '2020-02-31' AS CountFebuary, count(AccNum) Where date between 2020-03-01 AND '2020-02-31' AS CountMarch,
FROM Table
Where date BETWEEN '2020-02-01' AND '2020-03-31'
AND
group IN ('groupA','groupB')
I want to be able to get the total count of accounts for each month without writing a separate query for it. Is that possible?
You can do conditional aggregation:
select
sum(case when date >= '2020-02-01' and date < '2020-03-01' then 1 else 0 end) cnt_february,
sum(case when date >= '2020-03-01' and date < '2020-04-01' then 1 else 0 end) cnt_march
from mytable
where
date >= '2020-02-01' and date < '2020-04-01'
and group IN ('groupA','groupB')
Since you only want two months of data, then we can shorten the conditional expressions a little:
select
sum(case when date < '2020-03-01' then 1 else 0 end) cnt_february,
sum(case when date >= '2020-03-01' then 1 else 0 end) cnt_march
from mytable
where
date >= '2020-02-01' and date < '2020-04-01'
and group IN ('groupA','groupB')
If you are running MySQL, we can shorten some more:
select
sum(date < '2020-03-01') cnt_february,
sum(date >= '2020-03-01') cnt_march
from mytable
where
date >= '2020-02-01' and date < '2020-04-01'
and group IN ('groupA','groupB')
Side note: group is a reserved word in most databases, hence not a good choice for a column name.

JOIN two SELECT statement results from two different tables

I am trying to create a code to join two statements each from different table and conditions, as follows:
the first statement:
select TO_CHAR(Entry_date, 'MON.YYYY') AS Months, count(Customer_id) "Count Customer"
from table1
where entry_date >= TO_DATE('01.01.1900', 'DD.MM.YYYY')
AND entry_date <= TO_DATE('31.12.2017', 'DD.MM.YYYY')
and Customer_status = 'Active'
group by TO_CHAR(entry_date,'MON.YYYY')
order by to_date(TO_CHAR(entry_date, 'MON.YYYY'),'MON.YYYY')
The second statement:
select count (order_id) "Order"
from table2
where leave_date >= TO_DATE('01.01.1900', 'DD.MM.YYYY')
AND leave_date <= TO_DATE('31.12.2017', 'DD.MM.YYYY')
group by TO_CHAR(leave_date,'MON.YYYY')
order by to_date(TO_CHAR(leave_date, 'MON.YYYY'),'MON.YYYY')
the result should look like this
Months Count Customer Order
Jan. 2017 15 0
Feb. 2017 1 8
Mar. 2017 30 10
The order should be dependent on the Months that were stated in the first statement.
Thanks for your help in advance.
I would write this as:
select yyyymm, sum(cust_count) as cust_count, sum(num_orders) as num_orders
from ((select to_char(entry_date, 'YYYY-MM') as yyyymm, count(*) as cust_count, 0 as num_orders
from table1
where entry_date >= date '1900-01-01' and
entry_date < date '2018-01-01' and
Customer_status = 'Active'
group by to_char(entry_date, 'YYYY-MM')
) union all
(select to_char(leave_date, 'YYYY-MM') as yyyymm, 0,
count(*) as num_orders
from table2
where leave_date >= date '1900-01-01' and
leave_date < date '2018-01-31'
group by to_char(leave_date, 'YYYY-MM')
)
) tt
group by yyyymm
order by yyyymm;
Notes on some changes:
The use of date rather than to_char() with date constants.
The use of the format "YYYY-MM", which orders correctly. (You don't have to use it but it recommended.)
The union all brings all the data together. In Oracle, you can also use a full outer join, but that requires more use of coalesce().

Oracle sql query sum data every month

Let's say i have selected data from oktober until desember like this
i want to get sum of buying customer in every months (okt-des)
The result i want is like table below
i already know how to get last day of months but i don't have idea query in SQL to get result like i need
One way to do this - if you just need the data for those three months - is to use conditional aggregation:
select name,
sum(case when dt >= date '2017-10-01' and dt < date '2017-11-01'
then buying end) as oktober,
sum(case when dt >= date '2017-11-01' and dt < date '2017-12-01'
then buying end) as november,
sum(case when dt >= date '2017-12-01' and dt < date '2018-01-01'
then buying end) as desember
from YOUR_TABLE
where dt >= date '2017-10-01' and dt < date '2018-01-01'
group by name
;
Note that date is an Oracle keyword which should not be used as a column name; I changed it to dt. YOUR_TABLE should be your actual table name.
Try this
DESC TABLE_NAME
NAME VARCHAR2(20 BYTE)
BUYING NUMBER
BUYING_DATE DATE
select * from
(
select name,buying,RTRIM(to_char(buying_Date,'Month')) dd
from
TABLE_NAME
)
PIVOT
(
SUM(buying)
for dd IN ('October','November','December')
);

Multiple Counts from the same row with range

I am trying to get number of transactions within the week given start date and end date.The below query works fine for one day 2011-10-14
SELECT COUNT(operationId) AS trans
FROM hwfg_t_Tracking
WHERE hitTime BETWEEN '2011-10-14 00:00:00' AND '2011-10-14 23:59:59.99'
GO
How can I get the count on operationId where hitTime between 14,13,12,11,10,9,8(1 week) with the single SELECT statement. Like number of transactions for 2011-10-14 as a column, 2011-10-13 as another column and so on
Try the following query for one row per day in your specified range:
SELECT cast(hitTime AS date) AS mydate, COUNT(operationId) AS trans
FROM hwfg_t_Tracking
WHERE hitTime >= '2011-10-14 00:00:00' AND hitTime < '2011-10-21 00:00:00'
GROUP BY cast(hitTime AS date)
Or, if you want them all in one row:
SELECT COUNT(*) AS total_sum
,COUNT(CASE WHEN cast(dt As date) = '2011-10-14' THEN 1 ELSE NULL END) AS day14
,COUNT(CASE WHEN cast(dt As date) = '2011-10-15' THEN 1 ELSE NULL END) AS day15
,COUNT(CASE WHEN cast(dt As date) = '2011-10-16' THEN 1 ELSE NULL END) AS day16
-- etc.
FROM hwfg_t_Tracking
WHERE hitTime >= '2011-10-14 00:00:00' AND hitTime < '2011-10-21 00:00:00'