I'm just wondering how can I join these two queries?
SELECT SUM(TOTAL_SALES) FROM TBL_SALES WHERE LOGIN_DATE >= '2020-01-03' AND LOGOUT_DATE < '2020-01-04'
SELECT SUM(TOTAL_MONEY) FROM TBL_SALES_INFO WHERE LOGIN_DATE >= '2020-01-03' AND LOGOUT_DATE < '2020-01-04'
here's my code but I'm getting the wrong result:
SELECT SUM(A.TOTAL_SALES) AS TOTAL_SALES,
SUM(B.TOTAL_MONEY) AS TOTAL_MONEY
FROM TBL_SALES A RIGHT JOIN
TBL_SALES_INFO B
ON A.SALES_NO= B.SALES_NO
WHERE A.LOGIN_DATE>= '2020-01-01 09:00:00' AND A.LOGOUT_DATE < '2020-01-02 09:00:00' AND
B.LOGIN_DATE>= '2020-01-01 09:00:00' AND B.LOGOUT_DATE < '2020-01-02 09:00:00'
Your current queries are an entirely separate tables. The best I can see would be to just leave them as is, an aggregate in a top level SELECT:
SELECT
(SELECT SUM(TOTAL_SALES) FROM TBL_SALES
WHERE LOGIN_DATE >= '2020-01-03' AND LOGOUT_DATE < '2020-01-04') AS TOTAL_SALES,
(SELECT SUM(TOTAL_MONEY) FROM TBL_SALES_INFO
WHERE LOGIN_DATE >= '2020-01-03' AND LOGOUT_DATE < '2020-01-04') AS TOTAL_MONEY;
You might be over-complicating things with the a. and b. stuff? In addition, rather than a right join maybe an inner join would work better? Then you wouldn't need to sort by timestamps from both tables. You'd only need the total sales and timestamps based on one tables time.
It would help if you posted the results you were getting, even if they were incorrect.
Related
I am trying to get orders from between two dates and where those orders were between 5PM and 6:20 PM.
SELECT CREATION_DATE_TIME_STAMP
FROM SHIPMENT_HEADER
WHERE CREATION_DATE_TIME_STAMP between '2019-08-01 17:00:00:000' and '2019-10-31 18:20:000' AND
ORDER_TYPE = 'Catalog'
You need separate conditions on the date and time components. Date and time functions are notoriously database specific, but something like this:
SELECT CREATION_DATE_TIME_STAMP
FROM SHIPMENT_HEADER
WHERE CREATION_DATE_TIME_STAMP >= '2019-08-01' AND
CREATION_DATE_TIME_STAMP < '2019-11-01' AND
CAST(CREATION_DATE_TIME_STAMP as TIME) >= '17:00:00' AND
CAST(CREATION_DATE_TIME_STAMP as TIME) <= '18:20:00' AND
ORDER_TYPE = 'Catalog';
This assumes that a simple cast() is sufficient to extract the time.
Self-taught beginner here.
I want to sort out (old) clients that have last transactions in 2014 but nothing else till date.
I tried this:
select distinct transact_clntab.chn, transact_clntab.TRADE_DATE, transact_clntab.acct_code from transact_clntab
WHERE Trade_date not between '2019-01-01' and '2019-12-31'
and TRADE_DATE not between '2018-01-01' and '2018-12-31'
and TRADE_DATE not between '2017-01-01' and '2017-12-31'
and TRADE_DATE not between '2016-01-01' and '2016-12-31'
and TRADE_DATE not between '2015-01-01' and '2015-12-31'
and not exists
(SELECT chn, TRADE_DATE, acct_code FROM transact_clntab
WHERE Trade_date between '2008-01-01' and '2019-12-31'
and TRADE_DATE between '2009-01-01' and '2018-12-31'
and TRADE_DATE between '2010-01-01' and '2017-12-31'
and TRADE_DATE between '2011-01-01' and '2016-12-31'
and TRADE_DATE between '2012-01-01' and '2015-12-31'
and TRADE_DATE between '2013-01-01' and '2017-12-31'
and TRADE_DATE between '2014-01-01' and '2016-12-31')
but get no result
THEN
I tried sorting with excel but even more difficult
SELECT chn, TRANTYPE, ACCT_CODE, TRADE_DATE FROM transact_clntab
where TRADE_DATE < '2016-01-01'
order by TRADE_DATE
I expect the output to be
chn Transactiontype Account code Trade date
7683592 SALES BABA-2688 2008-06-02 00:00:00.000
58987897 PURCHASE FASH-1492 2008-06-02 00:00:00.000
If you want clients whose last trade is in 2014, then use aggregation:
SELECT t.acct_code
FROM transact_clntab t
GROUP BY t.acct_code
HAVING MAX(t.trade_date) < '2015-01-01' AND
MAX(t.trade_date) >= '2014-01-01';
If you want information about the last transaction for such accounts, use window functions:
SELECT t.*
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY t.acct_code ORDER BY t.trade_date DESC) as seqnum
FROM transact_clntab t
) t
WHERE seqnum = 1 AND
t.trade_date < '2015-01-01' AND
t.trade_date >= '2014-01-01';
Try grouping your data over the client id (I'm thinking it is chn) and fetch maximum date and put a condition that date is less than 2015.
SELECT transact_clntab.chn, MAX(transact_clntab.TRADE_DATE) last_transaction, transact_clntab.acct_code
FROM transact_clntab
GROUP BY transact_clntab.chn,transact_clntab.acct_code
HAVING last_transaction < '2015-01-01';
Hope this helps!
You're close. But you're not correlating by the customer, that's why the subquery to NOT EXISTS always finds some rows I guess. Your multiple BETWEENs can also be simplified and are probably better replaced by a check of an right open interval.
Assuming acct_code identifies the customer, you can try:
SELECT DISTINCT
tc1.chn,
tc1.trade_date,
tc1.acct_code
FROM transact_clntab tc1
WHERE tc1.trade_date >= '2014-01-01'
AND tc1.trade_date < '2015-01-01'
AND NOT EXISTS (SELECT *
FROM transact_clntab tc2
WHERE tc2.acct_code = tc1.acct_code
AND tc2.trade_date >= '2015-01-01');
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().
create table Minutes(Minute varchar2(5));
create table orders(OrderID varchar(54), Orderplaced TIMESTAMP ,
Ordercompleted TIMESTAMP);
insert into orders
VALUES
('#1',TO_TIMESTAMP('2018-01-15 00:12:20', 'YYYY-MM-DD HH24:MI:SS'),
TO_TIMESTAMP( '2018-01-15 00:12:42', 'YYYY-MM-DD HH24:MI:SS'));
insert into orders
VALUES
('#2',TO_TIMESTAMP('2018-01-15 01:15:20', 'YYYY-MM-DD HH24:MI:SS'),
TO_TIMESTAMP( '2018-01-15 02:56:20', 'YYYY-MM-DD HH24:MI:SS'));
insert into orders
VALUES
('#3',TO_TIMESTAMP('2018-01-15 01:20:20', 'YYYY-MM-DD HH24:MI:SS'),
TO_TIMESTAMP( '2018-01-15 03:00:20', 'YYYY-MM-DD HH24:MI:SS'));
insert into Minutes (Minute)
select to_char(trunc(sysdate) + interval '1' minute * (level - 1),
'HH24:MI') as minute
from dual
connect by level <= 1440;
select a.Minute, nvl(count(b.OrderID),0) as orders
from Minutes a
left join orders b
on a.Minute between to_char(cast( b.Orderplaced as date),'hh24:mi:ss') and
to_char(cast( b.Ordercompleted as date),'hh24:mi:ss')
where
a.Minute <= (select to_char(cast (sysdate as date),'hh24:mi:ss') from dual)
group by a.Minute
order by 1;
The processing time is too long and the result is undelivered as well.
It works fine with Integration testing. Please have a look once.
I ran your code, works OK for those test tables. However, I'd suggest a slight modification.
you don't have to CAST values from ORDERS table
Even worse is to CAST SYSDATE AS DATE - SYSDATE is a function that
returns DATE data type anyway
there's no need to select it from DUAL - you can use it "as is"
COUNT will return 0 even if there's nothing to return, so you can
omit NVL function
Here's the modified SELECT statement:
SELECT a.minute, COUNT (b.orderid) AS orders
FROM minutes a
LEFT JOIN orders b
ON a.minute BETWEEN TO_CHAR (b.orderplaced, 'hh24:mi:ss')
AND TO_CHAR (b.ordercompleted, 'hh24:mi:ss')
WHERE a.minute <= TO_CHAR (SYSDATE, 'hh24:mi:ss')
GROUP BY a.minute
ORDER BY 1;
What does it mean for you? I don't know. As I said, it works OK. Explain plan says that it performs full table scan of both MINUTES and ORDERS tables, so - if there's zillion rows in those tables, it might make a difference.
Consider creating indexes on columns you use; as you extract only time from the ORDERS table, those two would be function-based ones.
CREATE INDEX i1_min
ON minutes (minute);
CREATE INDEX i2_plac
ON orders (TO_CHAR (orderplaced, 'hh24:mi:ss'));
CREATE INDEX i3_compl
ON orders (TO_CHAR (ordercompleted, 'hh24:mi:ss'));
Then try again; hopefully, you'll see some improvement.
You said you're trying to get "the number of orders count per minute on a particular day", and later clarified that should be the current day. Your query is only looking at times - converted to strings - so it's looking at the same time slot across all records in your orders table. Really you want to restrict the found orders to the day you're interested in. Presumably your UAT environment just has much more data, across more days, than you created in IT.
You could just add a filter to restrict it to orders placed today:
select a.Minute, nvl(count(b.OrderID),0) as orders
from Minutes a
left join orders b
on a.Minute between to_char(cast( b.Orderplaced as date),'hh24:mi:ss') and
to_char(cast( b.Ordercompleted as date),'hh24:mi:ss')
and b.Orderplaced > trunc(sysdate) -- added this filter
where
a.Minute <= (select to_char(cast (sysdate as date),'hh24:mi:ss') from dual)
group by a.Minute
order by 1;
though you don't need any of the casting or subquery or nvl(), as #Littlefoot mentioned, so can simplify that a bit to:
select a.Minute, count(b.OrderID) as orders
from Minutes a
left join orders b
on a.Minute between to_char(b.Orderplaced,'hh24:mi:ss') and
to_char(b.Ordercompleted,'hh24:mi:ss')
and b.Orderplaced > trunc(sysdate)
where a.Minute <= to_char(sysdate,'hh24:mi:ss')
group by a.Minute
order by 1;
You're still doing a lot of conversions and comparing strings rather than dates/timestamps. It might be simpler to generate the minutes for that specific day in a CTE instead of a permanent table, and join using those values as well, without doing any further data conversions
with minutes (minute) as (
select cast(trunc(sysdate) as timestamp) + interval '1' minute * (level - 1)
from dual
connect by level <= (sysdate - trunc(sysdate)) * 1440
)
select to_char(m.minute, 'HH24:MI') as minute, count(o.orderid) as orders
from minutes m
left join orders o
on o.orderplaced >= cast(trunc(sysdate) as timestamp)
and o.orderplaced <= m.minute
and (o.ordercompleted is null or o.ordercompleted >= m.minute)
group by m.minute
order by m.minute;
I've included rows with no ordercompleted date, though it isn't clear if you want to count those.
You could also join on just the orderplaced date being today, which looks a bit odd, and do a conditional count:
with minutes (minute) as (
select cast(trunc(sysdate) as timestamp) + interval '1' minute * (level - 1)
from dual
connect by level <= (sysdate - trunc(sysdate)) * 1440
)
select to_char(m.minute, 'HH24:MI') as minute,
count(case when o.orderplaced <= m.minute
and (o.ordercompleted is null or o.ordercompleted >= m.minute)
then o.orderid end) as orders
from minutes m
left join orders o
on o.orderplaced >= cast(trunc(sysdate) as timestamp)
group by m.minute
order by m.minute;
Either way this assumes you have an index on orderplaced.
Look at the execution plans for your original query and these options and any others suggested, and test with realistic data, to see which is the best approach for your actual data and requirements.
To look for records for a different, full, day, change the sysdate references to a date/timestamp literal like timestamp '2018-01-15 00:00:00' or something relative like trunc(sysdate-1), and include an end-date on the orderplaced; and remove the end-time filter in the CTE; e.g.:
with minutes (minute) as (
select cast(trunc(sysdate - 1) as timestamp) + interval '1' minute * (level - 1)
from dual
connect by level <= 1440
)
select to_char(m.minute, 'HH24:MI') as minute, count(o.orderid) as orders
from minutes m
left join orders o
on o.orderplaced >= cast(trunc(sysdate - 1) as timestamp)
and o.orderplaced < cast(trunc(sysdate - 1) as timestamp) + interval '1' day
and o.orderplaced <= m.minute
and (o.ordercompleted is null or o.ordercompleted >= m.minute)
group by m.minute
order by m.minute;
or
with minutes (minute) as (
select timestamp '2018-01-15 00:00:00' + interval '1' minute * (level - 1)
from dual
connect by level <= 1440
)
select to_char(m.minute, 'HH24:MI') as minute, count(o.orderid) as orders
from minutes m
left join orders o
on o.orderplaced >= timestamp '2018-01-15 00:00:00'
and o.orderplaced < timestamp '2018-01-16 00:00:00'
and o.orderplaced <= m.minute
and (o.ordercompleted is null or o.ordercompleted >= m.minute)
group by m.minute
order by m.minute;
If you want to include rows where the placed and completed times are in the same minute, but still otherwise want to exclude rows from the minute they were placed, you'll need a bit more logic; maybe something like:
with minutes (minute) as (
select timestamp '2018-01-15 00:00:00' + interval '1' minute * (level - 1)
from dual
connect by level <= 1440
)
select to_char(m.minute, 'HH24:MI') as minute, count(o.orderid) as orders
from minutes m
left join orders o
on o.orderplaced >= timestamp '2018-01-15 00:00:00'
and o.orderplaced < timestamp '2018-01-16 00:00:00'
and ((trunc(o.ordercompleted, 'MI') > trunc(o.orderplaced, 'MI')
and o.orderplaced <= m.minute)
or (trunc(o.ordercompleted, 'MI') = trunc(o.orderplaced, 'MI')
and o.orderplaced < m.minute + interval '1' minute))
and (o.ordercompleted is null or o.ordercompleted >= m.minute)
group by m.minute
order by m.minute;
If you need further refinements you'll need to modify the clauses to suit, which might need a bit of experimentation.
I have a large table in MS SQL 2012 (40m records) containing call data. I would like to find the peak volume of calls, and the time that it occurred. If possible, I would also like to find the next 4 busiest periods.
I plan to use 3 columns:
CallID
DialTime
EndTime
The only way I can think to do this would be to do this:
Select '2013-07-01 00:00:01' as [Period], count([CallID]) as [Calls]
from [Table]
where DialTime <= '2013-07-01 00:00:01'
and EndTime >= '2013-07-01 00:00:01'
union
Select '2013-07-01 00:00:02' as [Period], count([CallID]) as [Calls]
from [Table]
where DialTime <= '2013-07-01 00:00:02'
and EndTime >= '2013-07-01 00:00:02'
union
etc
Can anyone suggest a better/more efficient way of doing this?
Try something like this. #time_begin and #time_end are the parameters that you can use for the interval of time for which you want to get the results.
with time_items (time_item) as
(
select #time_begin as time_item
union all
select dateadd(second,1,t.time_item) as time_item from time_items t where t.time_item<#time_end
)
select
time_items.time_item as [Period],
sum(case when [Table].DialTime<=time_items.time_item and [Table].EndTime>=time_items.time_item then 1 else 0 end) as [Calls]
from time_items
left outer join [Table] on 1=1
group by
time_items.time_item
order by
[Calls] desc;
You can use VALUES as Table Source
SELECT DialTime, EndTime, o.Calls
FROM (VALUES ('20130701 00:00:01', '20130701 00:00:01'),
('20130701 00:00:02', '20130701 00:00:02'))x(DialTime, EndTime)
CROSS APPLY(
SELECT COUNT(CallID) AS Calls
FROM [Table] t
WHERE DialTime <= x.DialTime
AND EndTime >= x.EndTime
) o