MySQL Merge two queries based on mutual column - mysql

I have two queries that retrieve records from 2 different tables that are almost alike and I need to merge them together.
Both have created_date which is of type datetime and I'm casting this column to date because I want to group and order them by date only, I don't need the time.
First query:
select cast(created_date as date) the_date, count(*)
from question
where user_id = 2
group by the_date
order by the_date;
+------------+----------+
| the_date | count(*) |
+------------+----------+
| 2021-01-02 | 1 |
| 2021-02-10 | 1 |
| 2021-02-14 | 5 | -- this line contains a mutual date
| 2021-03-16 | 1 |
| 2021-03-26 | 3 |
| 2021-03-27 | 23 |
| 2021-03-28 | 5 |
| 2021-03-29 | 1 |
+------------+----------+
Second query:
select cast(created_date as date) the_date, count(*)
from answer
where user_id = 2
group by the_date
order by the_date;
+------------+----------+
| the_date | count(*) |
+------------+----------+
| 2021-02-08 | 2 |
| 2021-02-14 | 1 | -- this line contains a mutual date
| 2021-04-05 | 5 |
| 2021-04-06 | 2 |
+------------+----------+
What I need is to merge them like this:
+------------+---------------+---------------+
| the_date | count(query1) | count(query2) |
+------------+---------------+---------------+
| 2021-01-02 | 1 | 0 | -- count(query2) is 0 bc. it's not in the second query
| 2021-02-08 | 0 | 2 | -- count(query1) is 0 bc. it's not in the first query
| 2021-02-10 | 1 | 0 |
| 2021-02-14 | 5 | 1 | -- mutual date
| 2021-03-16 | 1 | 0 |
| 2021-03-26 | 3 | 0 |
| 2021-03-27 | 23 | 0 |
| 2021-03-28 | 5 | 0 |
| 2021-03-29 | 1 | 0 |
| 2021-04-05 | 0 | 5 |
| 2021-04-06 | 0 | 2 |
+------------+---------------+---------------+
Basically what I need is to have all dates together and for each date to have the corresponding values from those two queries.

try something like this.
SELECT the_date , max(cnt1) , max(cnt2)
FROM (
select cast(created_date as date) the_date, count(*) AS cnt1 , 0 as cnt2
from question
where user_id = 2
group by the_date
order by the_date
UNION ALL
select cast(created_date as date) the_date, 0, count(*)
from answer
where user_id = 2
group by the_date
order by the_date
) as t1
GROUP BY the_date
ORDeR BY the_date;

Related

Retrieving data from the row containing the closest date to today with whereDate

I want to sort by price after filtering and grouping by date. However, because there are more than one relation, I cannot get the result I want.
The result I want is to get the price of the relation that is the closest to the end_date and sort it accordingly.
For this, the query, sql output, tables and demo page are as follows.
Thanks in advance ..
demo sqlfiddle
$query->join('tableB', 'tableA.id', '=', 'tableB.pro_id')
->select('tableA.*', 'tableB.start_date', 'tableB.end_date', 'tableB.old_daily')
->where(function($sq) {
$today = Carbon::now()->format('Y-m-d');
$sq->whereDate('end_date', '>=', $today);
})
->groupBy('tableA.id')
->orderBy('price', desc);
Query:
select `tableA`.*, `tableB`.`start_date`, `tableB`.`end_date`, `tableB`.`price`
from `tableA`
inner join `tableB` on `tableA`.`id` = `tableB`.`pro_id`
where (date(`end_date`) >= 2021-03-07)
group by `tableA`.`id`
order by `price` desc
tableA
| id | title |
|----|-------|
| 1 | pro1 |
| 2 | pro2 |
| 3 | pro3 |
tableB
| id | start_date | end_date | price | pro_id |
|----|------------|------------|-------|--------|
| 1 | 2021-06-01 | 2021-06-05 | 750 | 2 |
| 2 | 2021-05-01 | 2021-05-05 | 850 | 2 |
| 3 | 2021-04-01 | 2021-04-05 | 650 | 2 |
| 4 | 2021-06-01 | 2021-06-05 | 2750 | 1 |
| 5 | 2021-05-01 | 2021-05-05 | 2850 | 1 |
| 6 | 2021-04-01 | 2021-04-05 | 2650 | 1 |
| 7 | 2021-06-01 | 2021-06-05 | 1750 | 3 |
| 8 | 2021-05-01 | 2021-05-05 | 1850 | 3 |
| 9 | 2021-04-01 | 2021-04-05 | 1650 | 3 |
this query gives the result you want.
It would be a good choice to use "right join" in this step.
sqlfiddle
select `tableA`.*, `tableB`.`start_date`, `tableB`.`end_date`, `tableB`.`price`
from `tableA`
right join(
SELECT id, start_date, end_date, pro_id, price, DATEDIFF(`tableB`.`end_date`, '2021-03-07') diff
FROM `tableB`
GROUP BY id order by diff asc
) `tableB` on `tableA`.`id` = `tableB`.`pro_id`
where (date(`end_date`) >= '2021-03-07')
group by `tableA`.`id`
order by `price` desc
the closest to the end_date and sort it accordingly.
you should find the difference between the given date and end date then sort ascendingly.
ORDER BY DATEDIFF(end_date, '2021-03-07') ASC

SQL - Select records that their columns do not follow the same order

Given we have following table where the series number and the the date should increment
+----+--------+------------+
| id | series | date |
+----+--------+------------+
| 1 | 10 | 2020-08-13 |
| 2 | 9 | 2020-08-02 |
| 3 | 8 | 2020-06-23 |
| 4 | 7 | 2020-06-08 |
| 5 | 6 | 2020-05-20 |
| 6 | 5 | 2020-05-05 |
| 7 | 4 | 2020-05-01 |
+----+--------+------------+
Is there a way to check if there are records that do not follow this pattern ?
For example row 2 has bigger series number but it's date is before row 3
+----+--------+------------+
| id | series | date |
+----+--------+------------+
| 1 | 10 | 2020-08-13 |
| 2 | 9 | 2020-06-02 |
| 3 | 8 | 2020-07-23 |
| 4 | 7 | 2020-06-08 |
| 5 | 6 | 2020-05-20 |
| 6 | 5 | 2020-05-05 |
| 7 | 4 | 2020-05-01 |
+----+--------+------------+
You can use window functions:
select *
from (
select t.*, lead(date) over(order by series) lead_date
from mytable t
) t
where date > lead_date
Alternatively:
select *
from (
select t.*, lead(series) over(order by date) lead_series
from mytable t
) t
where series > lead_series
You can use lag():
select t.*
from (select t.*,
lag(id) over (order by series) as prev_id_series,
lag(id) over (order by date) as prev_id_date
from t
) t
where prev_id_series <> prev_id_date;
You can fetch problematic rows and their corresponding conflicting rows using SELF JOIN like this (assuming your table is called "series"):
SELECT s1.id AS row_id, s1.series AS row_series, s1.date AS row_date,
s2.id AS conflict_id, s2.series AS conflict_series, s2.date AS conflict_date
FROM series AS s1
JOIN series AS s2
ON s1.series > s2.series AND s1.date < s2.date;

Select complete record with earliest timestamp on a day for each employee [duplicate]

This question already has answers here:
Group by minimum value in one field while selecting distinct rows
(10 answers)
Closed 2 years ago.
I have a table that stores facial login data of employees based upon employee id. I need to get the earliest login for each employee on a day and all other logins to be ignored. I know how to get latest or earliest record for each employee but I am unable to figure out how to get earliest entry in each day by each employee.
+----+-----------+--------------------------------------+-------------+-----------------------+
| id | camera_id | image_name | employee_id | created_at |
+----+-----------+--------------------------------------+-------------+-----------------------+
| 10 | 2 | pjcc7vf142pec6li7k8kqxuqvnmhm0tyo8ib | 16 | 2020-07-11 10:40:20 |
| 11 | 2 | 9iizfdtk3m81a745ut7tzqzqh8kf9ipz2u02 | 2 | 2020-07-11 10:40:22 |
| 14 | 2 | 3p74yrq35nfaazwdo8auguvn2h5hpugtfvvw | 2 | 2020-07-11 12:07:24 |
| 15 | 2 | hpa2am40ufke7o7q2y733hh83h7ykxxdgkof | 16 | 2020-07-11 12:09:35 |
| 16 | 2 | g7adgyzloab2t4z7xx2id0a9cjqx8ojfni99 | 2 | 2020-07-11 12:09:41 |
| 17 | 2 | tapufkiuj5toxfdoikjicbe3k7tl32yj5khp | 16 | 2020-07-12 12:09:47 |
| 18 | 2 | pjcc7vf142pec6li7k8kqxuqvnmhm0tyo8ib | 16 | 2020-07-12 14:40:20 |
| 19 | 2 | 9iizfdtk3m81a745ut7tzqzqh8kf9ipz2u02 | 2 | 2020-07-12 15:40:22 |
| 20 | 2 | 3p74yrq35nfaazwdo8auguvn2h5hpugtfvvw | 2 | 2020-07-12 16:07:24 |
| 21 | 2 | hpa2am40ufke7o7q2y733hh83h7ykxxdgkof | 16 | 2020-07-12 17:09:35 |
| 22 | 2 | g7adgyzloab2t4z7xx2id0a9cjqx8ojfni99 | 2 | 2020-07-13 12:09:41 |
+----+-----------+--------------------------------------+-------------+-----------------------+
The result will look like below...
+----+-----------+--------------------------------------+-------------+-----------------------+
| id | camera_id | image_name | employee_id | created_at |
+----+-----------+--------------------------------------+-------------+-----------------------+
| 10 | 2 | pjcc7vf142pec6li7k8kqxuqvnmhm0tyo8ib | 16 | 2020-07-11 10:40:20 |
| 11 | 2 | 9iizfdtk3m81a745ut7tzqzqh8kf9ipz2u02 | 2 | 2020-07-11 10:40:22 |
| 17 | 2 | tapufkiuj5toxfdoikjicbe3k7tl32yj5khp | 16 | 2020-07-12 12:09:47 |
| 19 | 2 | 9iizfdtk3m81a745ut7tzqzqh8kf9ipz2u02 | 2 | 2020-07-12 15:40:22 |
| 22 | 2 | g7adgyzloab2t4z7xx2id0a9cjqx8ojfni99 | 2 | 2020-07-13 12:09:41 |
+----+-----------+--------------------------------------+-------------+-----------------------+
You can do:
select *
from t
where (employee_id, created_at) in (
select employee_id, min(created_at)
from t
group by employee_id, date(created_at)
)
how to get earliest entry in each day by each employee
You can filter with a correlated subquery:
select t.*
from mytable t
where t.created_at = (
select min(t1.created_at)
from mytable t1
where
t1.employee_id = t.employee_id
and t1.created_at >= date(t.created_at)
and t1.created_at < date(t.created_at) + interval 1 day
)
This query would take advantage of an index on (employee_id, created_at).
Or, if you are running MySQL 8.0, you can use window functions:
select *
from (
select
t.*,
row_number() over(
partition by employee_id, date(created_at)
order by created_at
) rn
from mytable t
) t
where rn = 1

How to get zero count for missing dates in mysql group by clause

I have a mysql table from which i need to find how many records created per day in last 30 days.
A simple query for that is like
select count(*) from table_name where date(created) > yyyymmdd group by date(created) order by date(created)
I don't require to select date here, just the counts.
So i expect 30 rows here.
But the problem here is if there are no records created on some day, it will not appear in the result and I'll have less than 30 rows.
Is there any other way to print zero (0) count for the dates where no records are created ?
current output :
+---------------+----------+
| date(created) | count(*) |
+---------------+----------+
| 2019-12-20 | 1504 |
| 2019-12-29 | 12 |
+---------------+----------+
Expected output :
+---------------+----------+
| date(created) | count(*) |
+---------------+----------+
| 2019-12-20 | 1504 |
| 2019-12-21 | 0 |
| 2019-12-22 | 0 |
| 2019-12-23 | 0 |
| 2019-12-24 | 0 |
| 2019-12-25 | 0 |
| 2019-12-26 | 0 |
| 2019-12-27 | 0 |
| 2019-12-28 | 0 |
| 2019-12-29 | 12 |
+---------------+----------+
you can use like this :
SET #date_min = '2019-12-20';
SET #date_max = '2019-12-29';
SELECT
date_generator.date as the_date,
IFNULL(COUNT(table_name.id), 0) as count
from (
select DATE_ADD(#date_min, INTERVAL (#i:=#i+1)-1 DAY) as `date`
from information_schema.columns,(SELECT #i:=0) gen_sub
where DATE_ADD(#date_min,INTERVAL #i DAY) BETWEEN #date_min AND #date_max
) date_generator
left join `table_name` on DATE(created) = date_generator.date
GROUP BY date;
so here I am creating a temporary table date_generator will dates in between of given date range and join to with your main table (transactions).
output as expected:
+---------------+----------+
| the_date | count |
+---------------+----------+
| 2019-12-20 | 1504 |
| 2019-12-21 | 0 |
| 2019-12-22 | 0 |
| 2019-12-23 | 0 |
| 2019-12-24 | 0 |
| 2019-12-25 | 0 |
| 2019-12-26 | 0 |
| 2019-12-27 | 0 |
| 2019-12-28 | 0 |
| 2019-12-29 | 12 |
+---------------+----------+
Test:
WITH RECURSIVE
cte AS ( SELECT #yyyymmdd + INTERVAL 1 DAY dateCreated
UNION ALL
SELECT dateCreated + INTERVAL 1 DAY
FROM cte
WHERE dateCreated < ( SELECT MAX(DATE(created))
FROM table_name ) )
SELECT COUNT(table_name.created)
FROM cte
JOIN table_name ON DATE(table_name.created) = cte.dateCreated
GROUP BY cte.dateCreated
ORDER BY cte.dateCreated
#yyyymmdd is a variable or placeholder where the value from the condition of your query (where date(created) > yyyymmdd) must be specified.

MySQL Return Count for All Dates in Month, Including Those w/ Zero Results

I have a MySQL table that looks like this:
+--------+------------+------------------+
| id | account_id | posted_at |
+--------+------------+------------------+
| 1 | 1 | 2013-10-05 23:09 |
| 2 | 1 | 2013-10-07 14:24 |
| 3 | 1 | 2013-10-07 01:17 |
| 4 | 1 | 2013-10-09 06:58 |
+--------+------------+------------------+
For a particular account_id (in this case 1), I want to return this (for dates in the current month):
+--------+------------+
| count | date |
+--------+------------+
| 0 | 2013-10-01 |
| 0 | 2013-10-02 |
| 0 | 2013-10-03 |
| 0 | 2013-10-04 |
| 1 | 2013-10-05 |
| 0 | 2013-10-06 |
| 2 | 2013-10-07 |
| 0 | 2013-10-08 |
| 1 | 2013-10-09 |
+--------+------------+
I have a SQL query that returns the COUNTS for each date within this month.
SELECT
DATE(posted_at) AS formatted_date,
COUNT(id) AS count
FROM entries
WHERE account_id = 1
AND MONTH(DATE(posted_at)) = MONTH(NOW())
GROUP BY formatted_date
ORDER BY formatted_date ASC
It's just returning this:
+--------+------------+
| count | date |
+--------+------------+
| 1 | 2013-10-05 |
| 2 | 2013-10-07 |
| 1 | 2013-10-09 |
+--------+------------+
Of course, COUNT doesn't return anything for dates that have no data. I want the result to have a zero for dates with no data.
I've read that you should create a join table of all possible dates. Is this the only way?
You can try something like this
Declare #INT DATETIME = null
SELECT COUNT( CASE WHEN #INT IS NOT NULL THEN #INT ELSE NULL END)
Im not to sure about mysql but in sql server it would be something like this...
SELECT
DATE(posted_at) AS formatted_date,
COUNT( CASE WHEN IS NOT NULL THEN posted_at ELSE NULL END ) AS [count]
FROM entries
WHERE account_id = 1
AND MONTH(DATE(posted_at)) = MONTH(NOW())
GROUP BY formatted_date
ORDER BY formatted_date ASC