Optimized database for group by - mysql

I've got 2 tables, which look something like this
Device
device_id (PK)
other stuffs
Record (+-500k rows)
id (autoincrement PK)
device_id (FK)
date
direction (1 or 2)
amount
And I need queries which will be able to do following things
get sum of amounts of one device between two dates
get sum of amounts of all devices between two dates (group sum by device_id)
get sum of amounts of all devices between two dates and distinguish direction (group sum by device_id, direction)
My queries now look like this
SELECT SUM(amount) as total FROM Record WHERE device_id='anId' AND date BETWEEN min_date AND max_date
SELECT radar_id, SUM(amount) as total FROM Record WHERE date BETWEEN min_date AND max_date GROUP BY device_id
SELECT radar_id, SUM(amount) as total FROM Record WHERE date BETWEEN min_date AND max_date GROUP BY device_id, direction
the first query is completed in acceptable time (under 1s), but the other ones takes 30s+.
Is there a way to increase performance?

Related

SQL Moving window over two level of groupby

I have the following table of orders for users like the following:
CREATE TABLE orders (
order_id UUID,
user_id UUID,
date date,
order_type integer
);
I want to write SQL to do the following:
For every order want to compute the number of orders the user has within a previous week (7 days).
Write the following, but it counts the number of orders for each user but not for two levels of groupby.
SELECT order_id, user_id,
COUNT(order_id) OVER(PARTITION BY user_id ORDER BY date ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) as num_orders_7days
FROM orders
You should use RANGE clause instead of ROWS with the proper date intervals:
SELECT order_id, user_id, date,
COUNT(order_id) OVER (
PARTITION BY user_id
ORDER BY date
RANGE BETWEEN INTERVAL 7 day PRECEDING AND INTERVAL 1 day PRECEDING
) as num_orders_7days
FROM orders;
See the demo.

SQL query to find the cancellation rate of requests made between two dates using WITH

I'm trying to understand the right way to divide the count sums from two queries.
I'm teaching myself sql and practising it on line.
Question:
Write a SQL query to find the cancellation rate of requests made between 2017-08-01 and 2017-08-03. The cancellation rate is given by dividing the number of cancelled requests by the total number of rides each day. The result table should have 2 Columns, namely Day that shows each day and Cancellation Rate that provides the cancellation rate of that day.
Table is:
What I tried was:
count cancelled ride rates per date
count all ride requests per date
divide both the counts per date
with
cancelled_rides as
(select count(*) cancel_count, status, Request_id
from TRIPS
where status = 'cncld_driver'
group by state, Request_id)
all_rides as (
select count(*) day_count, status, Request_id
from TRIPS
group by state, Request_id) ,
select cancelled_rides.Request_id as DAY,
(cancelled_rides.cancel_count/all_rides.day_count) as 'Cancellation Rate'
FROM cancelled_rides, all_rides;
Does this look right? Note I purposefully ignored including date ranges as the table has only limited entries.
I do not see that a CTE helps at all for this query. Just use conditional aggregation:
select t.Request_id as day, count(*) as total,
sum( status = 'cncld_driver' ) as num_cancelled,
avg( status = 'cncld_driver' ) as cancellation_rate
from trips t
where request_id >= '2017-08-01' and
request_id < '2017-08-04'
group by request_id;
Calling a date "request_id" is rather confusing. You should have a request id that is unique for each row and a separate column with the date/time.

SQL query to find Sum of Amount from table Totals for all IDs for only Maximum Amounts

Totals table is
ID|Amount
1|10
1|20
2|30
2|40
3|50
4|60
5|70
5|80
Tried below SQL query to find sum of Amount from table Totals for all IDs for only Maximum Amounts:
select SUM(Amount) from Totals
where ID in (select Max(ID) from Totals
group by ID
order by ID desc);
Use a subquery that gets the maximum amount for each ID, then sum it.
SELECT SUM(amount) as totalMax
FROM (
SELECT MAX(amount) AS amount
FROM Totals
GROUP BY ID
) AS x

related to query using SQL

In oracle sql, how to get the count of newly added customers only for the month of april and may and make sure they werent there in the previous months
SELECT CUSTOMER ID , COUNT(*)
FROM TABLE
WHERE DATE BETWEEN '1-APR-2018' AND '31-MAY-2018' AND ...
If we give max (date) and min(date), we can compare the greater date to check if this customer is new , correct?
expected output is month count
april ---
may ---
should show the exact count how many new customers joined in these two months
One approach is to use aggregation:
select customer_id, min(date) as min_date
from t
group by customer_id
having min(date) >= date '2018-04-01 and
min(date) < date '2018-06-01';
This gets the list of customers (which your query seems to be doing). To get the count, just use count(*) and make this a subquery.

Joining tables that have no common value

I'm trying to join 3 tables for a system to keep track of what comes in and out of my shop.
The first tables is as following. (Production)
id
Amount
Date
item id
next table is: (sales)
id
date
amount
customer id
amount
the last is: (waste)
id
date
amount
reason
I haven't really found a way to join those 3 tables without using a common value they could join on. I need to order them by date so I can see the history of my income and expenses. If the 3 different tables could come with a individual value like 3 for waste 2 for sales and 1 for production would also be great.
What you want is not a join of the tables but a union: http://dev.mysql.com/doc/refman/5.0/en/union.html
Something like
(SELECT 'production' as source, id, Amount AS amount, Date AS date FROM Production)
UNION
(SELECT 'sales' as source, id, amount, date FROM sales)
UNION
(SELECT 'waste' as source, id, amount, date FROM waste)
ORDER BY date;