Include all the days when using limit? - mysql

I want want to get all rows from 1st August to 31 August but limit to 1500 rows, however, it must include all the possible days from 1 to 31. So from each day I expected around 48 rows.
How is that possible with SQL Query?
I have tried this SQL query but it wont include all the days because I am using limit.
SELECT * FROM table1
WHERE `submit` >= '2014-08-01' AND `submit` <= '2014-08-31'
order by `submit`
LIMIT 1500
Update:
It should get all the possible days from 1 to 31, if any days does not have enough rows - then include the remaining rows from any days. If there is no record on the 7th Aug for example, then any other days should include more rows.
Update2:
Let say submit >= '2014-08-01' AND submit <= '2014-08-31' there are 3000 rows but I only want 1000 rows (if possible) from 01 to 31.
Each day should have approx 32 rows.
For example
Aug 01 have 32 rows
Aug 02 have 32 rows
Aug 03 only 0 rows
Aug 04 only 0 rows
Aug 05 have 32 rows
to
Aug 31 have 32 rows

This is a troublesome requirement, because it calls for the unpredictable omission of data under unpredictable circumstances. This is the kind of thing that gives auditors bad dreams (not to mention programmers). It may help you to figure out what exactly you are trying to do with this result set, and tighten up your requirement.
Assuming your table has an autoincrementing unique id column, here's a query that will retrieve the last row from the table for each date.
SELECT 0 AS priority, t.* FROM table1 AS t
WHERE id IN (SELECT MAX(id) AS id FROM table1 GROUP BY DATE(submit))
You can then do this to get the remaining rows:
SELECT 1 AS priority, t.* FROM table1 AS t
WHERE id NOT IN (SELECT MAX(id) AS id FROM table1 GROUP BY DATE(submit))
You can take the union of these, sort them, and limit them.
SELECT *
FROM (
SELECT 0 AS priority, t.* FROM table1 AS t
WHERE id IN (SELECT MAX(id) AS id FROM table1 GROUP BY DATE(submit))
UNION ALL
SELECT 1 AS priority, t.* FROM table1 AS t
WHERE id NOT IN (SELECT MAX(id) AS id FROM table1 GROUP BY DATE(submit))
) AS results
ORDER BY priority, submit
LIMIT 1500
This will give you at least one row from each submit date, and then the rest of the rows, but never more than 1500. It makes no attempt to balance the number of rows from each date, however.

Step 1. The data table...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,dt DATE NOT NULL
);
INSERT INTO my_table (dt) VALUES
('2014-07-28'),
('2014-07-29'),
('2014-08-01'),
('2014-08-01'),
('2014-08-02'),
('2014-08-03'),
('2014-08-05'),
('2014-08-05'),
('2014-08-05'),
('2014-08-05'),
('2014-08-07'),
('2014-08-07'),
('2014-08-09'),
('2014-08-10'),
('2014-08-10'),
('2014-08-10'),
('2014-08-11'),
('2014-08-13'),
('2014-08-13'),
('2014-08-13'),
('2014-08-13'),
('2014-08-13'),
('2014-08-13'),
('2014-08-14'),
('2014-08-14'),
('2014-08-15'),
('2014-08-17'),
('2014-08-17'),
('2014-08-18'),
('2014-08-18'),
('2014-08-18'),
('2014-08-19'),
('2014-08-19'),
('2014-08-21'),
('2014-08-21'),
('2014-08-21'),
('2014-08-21'),
('2014-08-22'),
('2014-08-23'),
('2014-08-25'),
('2014-08-25'),
('2014-08-26'),
('2014-08-26'),
('2014-08-26'),
('2014-08-27'),
('2014-08-28'),
('2014-08-29'),
('2014-08-29'),
('2014-08-29'),
('2014-08-29'),
('2014-08-29'),
('2014-08-30'),
('2014-08-31'),
('2014-08-31');
SELECT * FROM my_table;
+----+------------+
| id | dt |
+----+------------+
| 1 | 2014-07-28 |
| 2 | 2014-07-29 |
| 3 | 2014-08-01 |
| 4 | 2014-08-01 |
| 5 | 2014-08-02 |
| 6 | 2014-08-03 |
| 7 | 2014-08-05 |
| 8 | 2014-08-05 |
| 9 | 2014-08-05 |
| 10 | 2014-08-05 |
| 11 | 2014-08-07 |
| 12 | 2014-08-07 |
| 13 | 2014-08-09 |
| 14 | 2014-08-10 |
| 15 | 2014-08-10 |
| 16 | 2014-08-10 |
...
...
| 44 | 2014-08-26 |
| 45 | 2014-08-27 |
| 46 | 2014-08-28 |
| 47 | 2014-08-29 |
| 48 | 2014-08-29 |
| 49 | 2014-08-29 |
| 50 | 2014-08-29 |
| 51 | 2014-08-29 |
| 52 | 2014-08-30 |
| 53 | 2014-08-31 |
| 54 | 2014-08-31 |
+----+------------+
Step 2. A calendar utility table holding all conceivable dates... (there are other ways of doing this, but - unusually - this problem requires that this is done here, in MySQL, rather than in post-processing)
SELECT *
FROM calendar
WHERE dt BETWEEN '2014-07-28' AND '2014-09-02';
+------------+
| dt |
+------------+
| 2014-07-28 |
| 2014-07-29 |
| 2014-07-30 |
| 2014-07-31 |
| 2014-08-01 |
| 2014-08-02 |
| 2014-08-03 |
| 2014-08-04 |
| 2014-08-05 |
| 2014-08-06 |
| 2014-08-07 |
| 2014-08-08 |
| 2014-08-09 |
| 2014-08-10 |
| 2014-08-11 |
| 2014-08-12 |
| 2014-08-13 |
| 2014-08-14 |
| 2014-08-15 |
| 2014-08-16 |
| 2014-08-17 |
| 2014-08-18 |
| 2014-08-19 |
| 2014-08-20 |
| 2014-08-21 |
| 2014-08-22 |
| 2014-08-23 |
| 2014-08-24 |
| 2014-08-25 |
| 2014-08-26 |
| 2014-08-27 |
| 2014-08-28 |
| 2014-08-29 |
| 2014-08-30 |
| 2014-08-31 |
| 2014-09-01 |
| 2014-09-02 |
+------------+
37 rows in set
Step 3. The query (guaranteeing at least one result per day and no more than 50 results overall)
SELECT dt FROM
(
SELECT a.dt
, COALESCE(rank,0)
FROM calendar a
LEFT
JOIN
( SELECT x.dt,COUNT(*) rank
FROM my_table x
JOIN my_table y
ON y.dt = x.dt
AND y.id <= x.id
GROUP
BY x.id
) b
ON b.dt = a.dt
WHERE a.dt BETWEEN '2014-08-01' AND '2014-08-31'
ORDER
BY rank,dt
LIMIT 50
)n
ORDER BY dt;
+------------+
| dt |
+------------+
| 2014-08-01 |
| 2014-08-01 |
| 2014-08-02 |
| 2014-08-03 |
| 2014-08-04 |
| 2014-08-05 |
| 2014-08-05 |
| 2014-08-05 |
| 2014-08-06 |
| 2014-08-07 |
| 2014-08-07 |
| 2014-08-08 |
| 2014-08-09 |
| 2014-08-10 |
| 2014-08-10 |
| 2014-08-10 |
| 2014-08-11 |
| 2014-08-12 |
| 2014-08-13 |
| 2014-08-13 |
| 2014-08-13 |
| 2014-08-14 |
| 2014-08-14 |
| 2014-08-15 |
| 2014-08-16 |
| 2014-08-17 |
| 2014-08-17 |
| 2014-08-18 |
| 2014-08-18 |
| 2014-08-18 |
| 2014-08-19 |
| 2014-08-19 |
| 2014-08-20 |
| 2014-08-21 |
| 2014-08-21 |
| 2014-08-21 |
| 2014-08-22 |
| 2014-08-23 |
| 2014-08-24 |
| 2014-08-25 |
| 2014-08-25 |
| 2014-08-26 |
| 2014-08-26 |
| 2014-08-27 |
| 2014-08-28 |
| 2014-08-29 |
| 2014-08-29 |
| 2014-08-30 |
| 2014-08-31 |
| 2014-08-31 |
+------------+
50 rows in set (0.00 sec)

Something crazy like this perhaps?
SELECT * FROM
(SELECT * FROM
(SELECT * FROM table1
WHERE `submit` = '2014-08-01'
LIMIT 48
UNION SELECT * FROM table1
WHERE `submit` = '2014-08-02'
LIMIT 48
UNION SELECT * FROM table1
WHERE `submit` = '2014-08-03'
LIMIT 48
UNION SELECT * FROM table1
WHERE `submit` = '2014-08-04'
LIMIT 48
etc...
)
UNION SELECT * FROM Table1
WHERE `submit` >= '2014-08-01' AND `submit` <= '2014-08-31'
)
order by `submit`
LIMIT 1500

Related

How to select the group of most recent records based on specific conditions in MySQL

I am looking for a way to select group of most recent records. I have two tables:
Table1:
+---------+-------------+--------+--------+------------------+
| user_id | category_id | field1 | field2 | date |
+---------+-------------+--------+--------+------------------+
| 19 | 2 | 3 | 3 | 22/01/2021 15:00 |
| 19 | 1 | 1 | 3 | 22/01/2021 15:00 |
| 19 | 5 | 2 | 2 | 22/01/2021 15:00 |
| 18 | 1 | 2 | 2 | 22/01/2021 15:04 |
| 18 | 31 | 4 | 1 | 22/01/2021 15:04 |
| 18 | 40 | 3 | 1 | 22/01/2021 15:04 |
| 19 | 40 | 2 | 2 | 22/01/2021 15:21 |
| 19 | 166 | 1 | 1 | 22/01/2021 15:21 |
| 19 | 40 | 5 | 5 | 22/01/2021 16:23 |
| 19 | 166 | 6 | 6 | 22/01/2021 16:23 |
+---------+-------------+--------+--------+------------------+
Table2:
+---------+-------------+
| user_id | category_id |
+---------+-------------+
| 18 | 1 |
| 18 | 31 |
| 18 | 40 |
| 19 | 40 |
| 19 | 166 |
+---------+-------------+
I would like to select the most recent rows from Table1 for specific user_id and category_id that exist in Table2. For, example if user_id=19, I would like to get the following group of records:
+---------+-------------+--------+--------+------------------+
| user_id | category_id | field1 | field2 | date |
+---------+-------------+--------+--------+------------------+
| 19 | 40 | 5 | 5 | 22/01/2021 16:23 |
| 19 | 166 | 6 | 6 | 22/01/2021 16:23 |
+---------+-------------+--------+--------+------------------+
My the most closest query is given the working fiddle, but that gives me the last four records of Table1.
This seems to do what you want for all users:
SELECT T1.*
FROM Table1 t1
WHERE T1.DATE = (SELECT MAX(tt1.DATE)
FROM Table1 tt1 JOIN
Table2 tt2
ON TT1.user_id = Tt2.user_id AND
tt1.category_id = tt2.category_id
WHERE tt1.user_id = t1.user_id AND
tt1.category_id = t1.category_id
)
You can add a filter in the outer query for a particular user.
I think the most efficient way is using Ties in sql :
SELECT TOP 1 WITH TIES t1.*
FROM Table1 t1 join Table2 t2 on t1.user_id=t2.user_id and t1.category_id=t2.category_id
ORDER BY ROW_NUMBER() OVER(PARTITION BY t1.[user_id],t1.[category_id] ORDER BY t1.date desc)

MYSQL - Get all months between two dates (from, to) and data for this months

I have table something like that:
| id | date | user_id | value |
---------------------------------------------
| 1 | 2019-01-10 | 3 | 20
| 2 | 2019-04-08 | 3 | 30
| 3 | 2019-06-04 | 3 | 40
| 4 | 2019-08-20 | 3 | 50
| 5 | 2019-11-19 | 3 | 60
| 6 | 2019-01-11 | 4 | 70
| 7 | 2019-02-20 | 4 | 11
| 8 | 2019-03-11 | 4 | 12
| 9 | 2019-07-12 | 4 | 23
--------------------------------
and I want to get values between two dates: date_from and date_to. And all months from this interval.
For example:
date_from = 2019-01-08;
date_to = 2019-09-10;
So for user_id = 3 i want to get something like that:
| date | value
-------------------------
| 2019-01 | 20 |
| 2019-02 | NULL |
| 2019-03 | NULL |
| 2019-04 | 30 |
| 2019-05 | NULL |
| 2019-06 | 40 |
| 2019-07 | NULL |
| 2019-08 | 50 |
| 2019-09 | NULL |
--------------------------
Is anyone help me? Thanks!
You can use a recursive CTE to generate the dates and then left join:
with recursive dates as (
select date('2019-01-08') as dte
union all
select dte + interval 1 day
from dates
where dte < '2019-09-10'
)
select extract(year_month from d.dte) as yyyymm, sum(t.value)
from dates d left join
t
on d.dte = t.date and t.user_id = 3
group by yyyymm;
Here is a db<>fiddle.

sql joins with multiple conditions

i have two tables, (say bill and soldproduct)
select * from bill;
+------+------------+------------+
| id | solddate | customerId |
+------+------------+------------+
| 11 | 2018-07-23 | 1 |
| 12 | 2018-07-21 | 1 |
| 13 | 2018-08-02 | 2 |
| 14 | 2018-08-08 | 2 |
| 15 | 2018-08-08 | 1 |
| 16 | 2018-08-08 | 1 |
+------+------------+------------+
select * from soldproduct;
+--------+-------------+----------+-------+------------+
| billid | productname | quantity | price | totalprice |
+--------+-------------+----------+-------+------------+
| 11 | book | 2 | 100 | 200 |
| 11 | pen | 10 | 10 | 100 |
| 11 | pencil | 5 | 2 | 10 |
| 12 | pencil | 5 | 2 | 10 |
| 13 | pen | 10 | 10 | 100 |
| 13 | book | 2 | 100 | 200 |
| 14 | pen | 1 | 10 | 10 |
| 14 | bottle | 1 | 75 | 75 |
| 15 | phone | 1 | 5000 | 5000 |
| 16 | lock | 15 | 50 | 750 |
+--------+-------------+----------+-------+------------+
I need to find the highest bill id using totalprice.
I tried using
select billid,sum(totalprice)
from soldproduct
where billid in (select id from bill where solddate >= date_sub(curdate(),interval 1 month))
group by billid
order by totalprice desc;
and my output is
+--------+-----------------+
| billid | sum(totalprice) |
+--------+-----------------+
| 15 | 5000 |
| 16 | 750 |
| 11 | 310 |
| 13 | 300 |
| 12 | 10 |
| 14 | 85 |
+--------+-----------------+
How do i get the same output with a single query using joins (without using subquery)?
try the following join
select billid,sum(totalprice)
from soldproduct
join bill on soldproduct.billid = bill.id and solddate >= date_sub(curdate(),interval 1
month)
group by billid
order by totalprice desc;
Can you try the below query:(I do not tested it out)
SELECT billid, SUM(totalprice)
FROM soldproduct SP
JOIN bill B ON (B.id = SP.billid)
WHERE B.solddate BETWEEN (CURRENT_DATE() - INTERVAL 1 MONTH) AND CURRENT_DATE()
GROUP BY SP.billid
ORDER BY SP.totalprice DESC;

Mysql - JOIN two tables group by 2 dates

I have two tables that are not linked by a foreign key but that I would like to join.
Here are the two functional SQL queries I would like to join:
SELECT DATE(added) as 'day', COUNT(*) as 'TopicSub'
FROM user_subscription
WHERE topic_id = 39
GROUP BY DATE(added)
SELECT DATE(date) as 'day', COUNT(*) as 'QSub'
FROM user_submitted_q
WHERE question_id IN (SELECT id FROM questions WHERE topic_id = 39)
GROUP BY DATE(date)
These two queries return a result including the total number of entries grouped by days.
First concern the days of the two queries do not necessarily match (There are no entries for all the days of the period), I would like to merge the results and have 0 if the other column does not include an equivalent date.
After a lot of unsuccessful attempts with all possible joints, I found this query that might work on PostgreSQL:
SELECT *
FROM
(SELECT DATE(date) as 'day', COUNT(*) as 'QSub'
FROM user_submitted_q
WHERE question_id IN (SELECT id FROM questions WHERE topic_id = 39)
GROUP BY DATE(date)) AS q1
FULL OUTER JOIN
(SELECT DATE(added) as 'day', COUNT(*) as 'TopicSub'
FROM user_subscription
WHERE topic_id = 39
GROUP BY DATE(added)) AS q2 ON q1.day = q2.day
ORDER BY
day
But unfortunately I use Mysql5.7
Here is the desired result:
Date | QSub | TopicSub
-----------+------+---------
2018-02-09 | 5 | 1
2018-02-19 | 19 | 13
2018-02-21 | 12 | 1
2018-02-22 | 43 | 0
2018-02-25 | 0 | 1
Sample user_submitted_q data:
+----+---------+-------------+--------+---------------------+----------------+
| id | user_id | question_id | result | date | ip |
+----+---------+-------------+--------+---------------------+----------------+
| 1 | 2 | 436 | good | 2018-02-09 00:13:15 | 176.159.30.253 |
| 2 | 2 | 409 | good | 2018-02-09 00:13:15 | 176.159.30.253 |
| 3 | 3 | 651 | wrong | 2018-02-09 00:13:53 | 77.136.14.187 |
| 4 | 3 | 651 | wrong | 2018-02-09 00:13:53 | 77.136.14.187 |
| 5 | 1 | 96 | wrong | 2018-02-09 00:21:51 | 77.69.200.124 |
| 6 | 1 | 24 | good | 2018-02-09 00:21:51 | 77.69.200.124 |
| 7 | 1 | 25 | good | 2018-02-09 00:21:51 | 77.69.200.124 |
| 8 | 1 | 96 | wrong | 2018-02-09 00:26:52 | 77.69.200.124 |
| 9 | 1 | 24 | good | 2018-02-09 00:26:52 | 77.69.200.124 |
| 10 | 1 | 25 | good | 2018-02-09 00:26:52 | 77.69.200.124 |
+----+---------+-------------+--------+---------------------+----------------+
Sample user_subscription data:
+----+---------+----------+---------------------+
| id | user_id | topic_id | added |
+----+---------+----------+---------------------+
| 8 | 1 | 39 | 2018-02-09 00:27:30 |
| 9 | 4 | 47 | 2018-02-09 00:42:34 |
| 10 | 4 | 19 | 2018-02-09 00:42:34 |
| 11 | 5 | 47 | 2018-02-09 00:54:14 |
| 13 | 6 | 47 | 2018-02-09 01:00:23 |
| 14 | 6 | 19 | 2018-02-09 01:00:23 |
| 17 | 8 | 47 | 2018-02-09 01:06:50 |
| 18 | 8 | 19 | 2018-02-09 01:06:50 |
| 19 | 9 | 47 | 2018-02-09 01:08:33 |
| 20 | 9 | 19 | 2018-02-09 01:08:33 |
+----+---------+----------+---------------------+
Thank you in advance for your help
Given
MariaDB [sandbox]> select * from t order by dt;
+------------+
| dt |
+------------+
| 2018-01-01 |
| 2018-01-02 |
| 2018-01-03 |
| 2018-01-03 |
| 2018-01-04 |
| 2018-01-04 |
+------------+
6 rows in set (0.00 sec)
MariaDB [sandbox]> select * from t1 order by dt;
+------------+
| dt |
+------------+
| 2018-01-02 |
| 2018-01-03 |
| 2018-01-04 |
| 2018-01-05 |
| 2018-01-05 |
| 2018-01-06 |
| 2018-01-06 |
| 2018-01-07 |
| 2018-01-07 |
+------------+
9 rows in set (0.00 sec)
select tu.dt,(select count(*) from t where t.dt = tu.dt) tdt,
(Select count(*) from t1 where t1.dt = tu.dt) t1dt
from
(
select dt from t
union
select dt from t1
) tu;
Will establish all dates spanning both tables then the sub queries will count them.
+------------+------+------+
| dt | tdt | t1dt |
+------------+------+------+
| 2018-01-01 | 1 | 0 |
| 2018-01-02 | 1 | 1 |
| 2018-01-03 | 2 | 1 |
| 2018-01-04 | 2 | 1 |
| 2018-01-05 | 0 | 2 |
| 2018-01-06 | 0 | 2 |
| 2018-01-07 | 0 | 2 |
+------------+------+------+
7 rows in set (0.00 sec)
My SQL does not support full outer joins but you can simulate that behavior as follows:
Select Col1, Col2, ...
From Table1
Left Join Table 2 on Table1.col = Table2.col
Union
Select Col1, Col2, ...
From Table 1
Right Join Table 2 on Table1.col = Table2.col
If you care to preserve duplicate rows you would change the Union to a Union All

MySQL select rows by max(column) by another column in SQL while using GROUP BY WEEK?

For each week I want to select the rows which have the highest weight for each distinct value of reps
-------------------------------------
| id | reps | weight | date |
| 1 | 1 | 15 | 2015-06-10 |
| 2 | 2 | 29 | 2015-06-12 |
| 3 | 1 | 30 | 2015-06-13 |
| 4 | 4 | 11 | 2015-06-14 |
| 5 | 1 | 15 | 2015-06-29 |
| 6 | 1 | 9 | 2015-06-30 |
and I would like it to return
-------------------------------------
| id | reps | weight | date |
| 2 | 2 | 29 | 2015-06-12 |
| 3 | 1 | 30 | 2015-06-13 |
| 4 | 4 | 11 | 2015-06-14 |
| 5 | 1 | 15 | 2015-06-29 |
I've tried
SELECT MAX(weight) as weight, reps, date, id FROM log_items
GROUP BY reps, WEEK(date)
But it seems to return completely random results, I have also tried using sub queries but they didn't work either (I'm guessing I was doing it wrong)
Try:
SELECT *
FROM log_items
WHERE (reps, WEEK(date), weight ) in (
SELECT reps, WEEK(date) , MAX(weight)
FROM log_items
GROUP BY reps, WEEK(date)
)
demo: http://sqlfiddle.com/#!9/4bf84/9