mySql query group by with order by date not working - php

I have two tables like this
and I want latest data by date
tbl_fruit_info
+-----+--------+
| sId | sName |
+-----+--------+
| 1 | Apple |
| 2 | Banana |
| 3 | Orange |
+-----+--------+
tbl_fruit_data
+------------+----------+-------------+
| date | fruit_id | fruit_price |
+------------+----------+-------------+
| 2017-01-26 | 1 | 44.7 |
| 2017-01-27 | 1 | 51 |
| 2017-01-25 | 1 | 71.5 |
| 2017-01-21 | 2 | 44 |
| 2017-01-23 | 2 | 55 |
| 2017-01-24 | 2 | 71 |
+------------+----------+-------------+
I tried with this
SELECT tbl_fruit_info.*, tbl_fruit_data.*
FROM tbl_fruit_data
LEFT JOIN
tbl_fruit_info ON tbl_fruit_info.sId = tbl_fruit_data.fruit_id
GROUP BY
tbl_fruit_info.sId
ORDER BY
max(tbl_fruit_data.date) DESC;
The Output I am looking for is like this:
+-----+--------+----------------------------+
| sId | sName | date |fruit_price |
+-----+--------+----------------------------+
| 1 | Apple | 2017-01-27 | 51 |
| 2 | Banana | 2017-01-24 | 71 |
| 3 | Orange | 0000-00-00 | 0 |
+-----+--------+----------------------------+
But I get this:
+-----+--------+----------------------------+
| sId | sName | date |fruit_price |
+-----+--------+----------------------------+
| 1 | Apple | 2017-01-26 | 44.7 |
| 2 | Banana | 2017-01-21 | 44 |
+-----+--------+----------------------------+
Any assistance would be greatly appreciated.

select * from tbl_fruit_data f
left join tbl_fruit_info i ON i.sId = f.fruit_id
where f.Date =
(Select max(date) from tbl_fruit_data
where fruit_id = f.fruit_id)

SELECT fi.*, fd.*
FROM tbl_fruit_data fd
FULL OUTER JOIN tbl_fruit_info fi ON fi.sId = fd.fruit_id
WHERE fd.fruit_id IS NULL OR fd.date = (
SELECT max(fd_inner.date)
FROM tbl_fruit_data fd_inner
WHERE fd_inner.fruit_id = fd.fruit_id
)
ORDER BY fd.date DESC;
Update 2017-01-30: Incorporates rows in tbl_fruit_info that don't have a corresponding row in tbl_fruit_data, now.

Ok here goes.
**Get the latest prices on fruit, ADDED fruits not having a price
SELECT d1.date, d1.fruit_id, d1.fruit_price, i.sId, i.sName
FROM tbl_fruit_info i
LEFT JOIN tbl_fruit_data d1
ON d1.fruit_id = i.sId
WHERE d1.date = (SELECT MAX(d2.date)
FROM tbl_fruit_data d2
WHERE d2.fruit_id = d1.fruit_id
)
UNION
SELECT CAST('0001-01-01' AS datetime) date, i.sId fruit_id, 0 fruit_price, i.sId, i.sName
FROM tbl_fruit_info i
WHERE i.sId NOT IN (SELECT d2.fruit_id FROM tbl_fruit_data d2)
Sql fiddle for test on the same data provides in question:
http://sqlfiddle.com/#!9/872ed4/9
Used these definitions
CREATE TABLE tbl_fruit_info
(`sId` int, `sName` varchar(6))
;
INSERT INTO tbl_fruit_info
(`sId`, `sName`)
VALUES
(1, 'Apple'),
(2, 'Banana'),
(3, 'Orange')
;
CREATE TABLE tbl_fruit_data
(`date` datetime, `fruit_id` int, `fruit_price` int)
;
INSERT INTO tbl_fruit_data
(`date`, `fruit_id`, `fruit_price`)
VALUES
('2017-01-26 00:00:00', 1, 44.7),
('2017-01-27 00:00:00', 1, 51),
('2017-01-25 00:00:00', 1, 71.5),
('2017-01-21 00:00:00', 2, 44),
('2017-01-23 00:00:00', 2, 55),
('2017-01-24 00:00:00', 2, 71)
;

Using dadde's fiddle (with some tweaks) - and assuming that the MySQL tag is correct...
CREATE TABLE tbl_fruit_info
(`sId` int auto_increment PRIMARY KEY, `sName` varchar(6))
;
INSERT INTO tbl_fruit_info
(`sId`, `sName`)
VALUES
(1, 'Apple'),
(2, 'Banana'),
(3, 'Orange')
;
CREATE TABLE tbl_fruit_data
(`date` datetime, `fruit_id` int, `fruit_price` int, primary key (fruit_id,date))
;
INSERT INTO tbl_fruit_data
(`date`, `fruit_id`, `fruit_price`)
VALUES
('2017-01-26 00:00:00', 1, 44.7),
('2017-01-27 00:00:00', 1, 51),
('2017-01-25 00:00:00', 1, 71.5),
('2017-01-21 00:00:00', 2, 44),
('2017-01-23 00:00:00', 2, 55),
('2017-01-24 00:00:00', 2, 71)
;
SELECT a.sid
, a.sname
, COALESCE(b.date,'0000-00-00') date
, COALESCE(b.fruit_price,0) fruit_price
FROM tbl_fruit_info a
LEFT
JOIN
( SELECT x.*
FROM tbl_fruit_data x
JOIN
( SELECT fruit_id, MAX(date) date
FROM tbl_fruit_data
GROUP
BY fruit_id
) y
ON y.fruit_id = x.fruit_id
AND y.date = x.date) b
ON b.fruit_id = a.sid;
+-----+--------+---------------------+-------------+
| sid | sname | date | fruit_price |
+-----+--------+---------------------+-------------+
| 1 | Apple | 2017-01-27 00:00:00 | 51 |
| 2 | Banana | 2017-01-24 00:00:00 | 71 |
| 3 | Orange | 0000-00-00 | 0 |
+-----+--------+---------------------+-------------+

create table #t1
(id int,name varchar(100));
create table #t2(
[date] date,
id int,
price float)
insert into #t1
select 1,'Apple'
union
select 2,'Banana'
insert into #t2
select getdate()-3,1,33.2
union
select getdate()-2,1,30.5
union
select getdate()-4,1,23.4
union
select getdate()-3,2,21
union
select getdate()-2,2,35
Query
select q.name,t2.* from (
select t1.id,t1.name,MAX(t2.date) as date from #t1 t1
inner join #t2 t2 on t1.id=t2.id
group by t1.id,t1.name
) as q inner join #t2 t2
on q.id=t2.id and t2.date=q.date
Edit: Fixed, replaced CTE with subquery, thanks dadde for pointing out CTE isn't supported in MySQL. CTE or Subquery does the same job here

Related

Mysql retrieve all nodes in a hierarchy and sum up their personal sales and group sales

I have a table with all the sales person and their sales manager with the following structure:
user_id, manager_id
2,1
3,1
4,3
5,2
6,5
If I look for manager 1, I will get a list of
2
3
4
5
6
As all of them are under manager 1
If I look for manager 2, I will get
5,
6
If I look for manager 3, I will get
4
I managed to get the list through the query below:
SELECT user_id
FROM (SELECT user_id, manager_id FROM tbl_member
ORDER BY manager_id, user_id) products_sorted,
(SELECT #pv := '" . $user_id . "') initialisation
WHERE FIND_IN_SET(manager_id, #pv) > 0
AND #pv := CONCAT(#pv, ',', user_id)
Now I have another sales table which has the following structure:
cust_id, sales_amount, user_id
Example data:
a001, 100, 2
a002, 200, 3
a003, 150, 1
a004, 200, 5
a005, 100, 4
a005, 80, 6
a006, 50, 1
Am I able to use JOIN with the sql query above so that I can get the data below:
1) Sum of sales_amount of a particular user_id
2) Total group sales of a user_id (Inclusive of all nodes, exclude personal sales)
Example if I look for user_id 1, I want to get the following data
format(user_id, personal_sales, group_sales)
2, 100, 280
3, 200, 100
4, 100, 0
5, 200, 80
6, 80, 0
Currently I am using multiple while loops to calculate the data...
$userSales = $this->getUserSales($rows['user_id'], $startDate, $endDate);
$groupSales = $this->getGroupSales($rows['user_id'], $startDate, $endDate);
In the getGroupSales function, I again run the below query
SELECT user_id
FROM (SELECT user_id, manager_id FROM tbl_member
ORDER BY manager_id, user_id) products_sorted,
(SELECT #pv := '" . $user_id . "') initialisation
WHERE FIND_IN_SET(manager_id, #pv) > 0
AND #pv := CONCAT(#pv, ',', user_id)
to retrieve all nodes below the user_id, and get the sum through getUserSales() function...which the execution time is insanely slow. With about 10,000 sales records and 1,000 user id, the page load is more than 30 seconds.
Can anyone advise how do I make the query runs faster?
You should run an EXPLAIN On your Query and see if all Indexes are used
For your SUM i would use fowwoing query and use the INDEXES you see in the exmaple.
You can also used combined Indexes for example for user_id and the time clumn that you didn't show.
CREATE TABLE table1 (
`atexdt` VARCHAR(4),
`amount` INTEGER,
`refuser_id` INTEGER
,INDEX(refuser_id)
);
INSERT INTO table1
(`atexdt`, `amount`, `refuser_id`)
VALUES
('a001', '100', '2'),
('a002', '200', '3'),
('a003', '150', '1'),
('a004', '200', '5'),
('a005', '100', '4'),
('a005', '80', '6'),
('a006', '50', '1');
CREATE TABLE u_m (
`user_id` INTEGER,
`manager_id` INTEGER
,INDEX(user_id)
,INDEX(manager_id)
);
INSERT INTO u_m
(`user_id`, `manager_id`)
VALUES
('2', '1'),
('3', '1'),
('4', '3'),
('5', '2'),
('6', '5');
✓
✓
✓
✓
SELECT
u1.user_id,
SUM(t1.amount)
,MAX(sumgroup)
FROM
table1 t1
INNER JOIN
u_m u1 ON t1.refuser_id = u1.user_id
INNER JOIN
(SELECT SUM(t1.amount) sumgroup, `manager_id` FROM table1 t1
INNER JOIN
u_m ON t1.refuser_id = u_m.user_id
GROUP BY `manager_id`) u2
ON u1.manager_id = u2.manager_id
WHERE u1.user_id IN (1,2,3,4,5,6)
GROUP BY u1.user_id
user_id | SUM(t1.amount) | MAX(sumgroup)
------: | -------------: | ------------:
2 | 100 | 300
3 | 200 | 300
4 | 100 | 100
5 | 200 | 200
6 | 80 | 80
db<>fiddle here
the EXPLAIN of that query
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra
-: | :---------- | :--------- | :--------- | :--- | :----------------- | :--------- | :------ | :------------------------ | ---: | -------: | :-------------------------------------------------
1 | PRIMARY | <derived2> | null | ALL | null | null | null | null | 5 | 100.00 | Using temporary; Using filesort
1 | PRIMARY | u1 | null | ALL | user_id,manager_id | null | null | null | 5 | 25.00 | Using where; Using join buffer (Block Nested Loop)
1 | PRIMARY | t1 | null | ref | refuser_id | refuser_id | 5 | db_1517119808.u1.user_id | 1 | 100.00 | null
2 | DERIVED | u_m | null | ALL | user_id,manager_id | null | null | null | 5 | 100.00 | Using where; Using temporary; Using filesort
2 | DERIVED | t1 | null | ref | refuser_id | refuser_id | 5 | db_1517119808.u_m.user_id | 1 | 100.00 | null

Mysql sort the results based on 2 columns from different tables

I have 2 tables with the following structure
Products
id | name | created_at |
1 | Produt1 | 2019-11-01 19:05:56 |
2 | Product 2 | 2020-01-28 19:05:56 |
3 | Product 3 | 2020-01-26 19:05:56 |
Draws
id | product_id |draw_number | created_at |
1 | 1 | 1 | 2020-01-28 19:05:56 |
2 | 1 | 2 | 2020-01-27 19:05:56 |
The scenario is, we have 3 products in product table and for product 1, we have 2 entries in draws table.
I am looking for a query here that selects the data from products table and data should be ordered by
If draws created_at exists, then order by draw created at
If no draws, sort by created at of products table.
Result output should be like this
id | name | created_at |
1 | Produt1 | 2019-01-28 19:05:56 | //created_at of draws of latest draw for this product
2 | Product 2 | 2020-01-28 19:05:56 |
3 | Product 3 | 2020-01-26 19:05:56 |
How can I selected the expected data?
TIA
Test
SELECT t1.*
FROM Products t1
LEFT JOIN ( SELECT t2.product_id,
MAX(t2.created_at) created_at
FROM Draws t2
GROUP BY product_id ) t3 ON t1.id = t3.product_id
ORDER BY GREATEST(t1.created_at, t3.created_at) DESC
This give you the wanted result
CREATE TABLE Draws
(`id` int, `product_id` int, `draw_number` int, `created_at` datetime)
;
INSERT INTO Draws
(`id`, `product_id`, `draw_number`, `created_at`)
VALUES
(1, 1, 1, '2020-01-28 19:05:56'),
(2, 1, 2, '2020-01-27 19:05:56')
;
✓
✓
CREATE TABLE Products
(`id` int, `name` varchar(9), `created_at` datetime)
;
INSERT INTO Products
(`id`, `name`, `created_at`)
VALUES
(1, 'Produt1', '2019-11-01 19:05:56'),
(2, 'Product 2', '2020-01-28 19:05:56'),
(3, 'Product 3', '2020-01-26 19:05:56')
;
✓
✓
SELECT p.id,p.name,MAX( IFNULL(d.`created_at` , p.`created_at`))
FROM Draws d RIGHT JOIN Products p
ON d.product_id = p.id
GROUP BY p.id,p.name
ORDER BY p.id;
id | name | MAX( IFNULL(d.`created_at` , p.`created_at`))
-: | :-------- | :--------------------------------------------
1 | Produt1 | 2020-01-28 19:05:56
2 | Product 2 | 2020-01-28 19:05:56
3 | Product 3 | 2020-01-26 19:05:56
db<>fiddle here

Use outer alias in subquery with 4 Tables in mysql

Hello guys I have 4 tables as an key-value store in mysql
t1 (article): t2:
| id | date | | id | key | value |
------------- ---------------------------
| 1 | 2016 | | 1 | title | title1 |
| 2 | 2017 | | 1 | user_id | 1 |
| 3 | 2018 | | 2 | title | title2 |
------------- | 2 | user_id | 2 |
| 3 | title | title3 |
| 3 | user_id | 1 |
---------------------------
t1 (user): t2:
| id | date | | id | key | value |
------------- -------------------------
| 1 | NULL | | 1 | name | user1 |
| 2 | NULL | | 2 | name | user2 |
------------- -------------------------
SELECT t1.id,
GROUP_CONCAT(IF(t2.key='title',t2.value,NULL)) AS title,
t1.date,
GROUP_CONCAT(IF(t2.key='user_id',t2.value,NULL)) AS user_id,
(
SELECT GROUP_CONCAT(IF(t4.key='user_name',t4.value,NULL))
FROM t4
GROUP BY t4.id
HAVING t4.id = user_id
) AS user_name
FROM t1
INNER JOIN t2
ON t1.id = t2.id
GROUP BY t1.id
i want to print out the name of the user that is stored as an id in t2 like:
| id | title | date | user_id | user_name |
------------------------------------------------
| 1 | title1 | 2016 | 1 | user1 |
| 2 | title2 | 2017 | 2 | user2 |
| 3 | title3 | 2018 | 1 | user1 |
------------------------------------------------
i have tested WHERE clause and HAVING clause, but nothing works for me.
I found your table references way too confusing, so I used an interpretation of the sample data. I only needed 3 of the 4 tables by the way. Demo
MySQL 5.6 Schema Setup:
CREATE TABLE articles
(`id` int, `date` date)
;
INSERT INTO articles
(`id`, `date`)
VALUES
(1, '2016-01-01'),
(2, '2017-01-01'),
(3, '2018-01-01')
;
CREATE TABLE users
(`id` int, `date` date)
;
INSERT INTO users
(`id`, `date`)
VALUES
(1, NULL),
(2, NULL)
;
CREATE TABLE t2_upper
(`id` int, `key` varchar(7), `value` varchar(6))
;
INSERT INTO t2_upper
(`id`, `key`, `value`)
VALUES
(1, 'title', 'title1'),
(1, 'user_id', '1'),
(2, 'title', 'title2'),
(2, 'user_id', '2'),
(3, 'title', 'title3'),
(3, 'user_id', '1')
;
CREATE TABLE t2_lower
(`id` int, `key` varchar(4), `value` varchar(5))
;
INSERT INTO t2_lower
(`id`, `key`, `value`)
VALUES
(1, 'name', 'user1'),
(2, 'name', 'user2')
;
Query 1:
select a.id, tn.value article_title, a.date, tu.id user_id, u.value user_name
from articles a
inner join (
select
*
from t2_upper
where `key` = 'title'
) tn on a.id = tn.id
inner join (
select
*
from t2_upper
where `key` = 'user_id'
) tu on a.id = tu.id
inner join (
select
*
from t2_lower
where `key` = 'name'
) u on tu.value = u.id
Results:
| id | article_title | date | user_id | user_name |
|----|---------------|------------|---------|-----------|
| 1 | title1 | 2016-01-01 | 1 | user1 |
| 2 | title2 | 2017-01-01 | 2 | user2 |
| 3 | title3 | 2018-01-01 | 3 | user1 |

How to get related row from same table?

I have a table like this:
// mytable
+----+---------+---------+
| id | related | color |
+----+---------+---------+
| 1 | 1 | red |
| 2 | 1 | blue |
| 3 | 3 | green |
| 4 | 1 | white |
| 5 | 3 | brown |
| 6 | 6 | gray |
| 7 | 3 | black |
| 8 | 1 | orange |
| 9 | 6 | pink |
+----+---------+---------+
I have an id number and I need to get the color of related id.
For example:
$id = 4; // I need to get `red`
$id = 5; // I need to get `green`
$id = 6; // I need to get `gray`
$id = 9; // I need to get `gray`
I can do that by using a JOIN. Something like this:
SELECT t2.color FROM mytable t1 JOIN mytable t2 ON t1.related = t2.id WHERE t1.id = :id
My query works as expected .. But I'm not sure using a JOIN for doing that is standard. Actually I'm trying to know is there any better approach? Or mine is a normal way?
What's wrong with SELECT t.related FROM mytable t WHERE t.id = :id? JOIN makes nothing more but checking if there is a actual id in 'related' column or not
I've done two different query and explain them, hope can give you some hints.
SQL Fiddle
MySQL 5.6 Schema:
CREATE TABLE mytable
(`id` int, `related` int, `color` varchar(6))
;
INSERT INTO mytable
(`id`, `related`, `color`)
VALUES
(1, 1, 'red'),
(2, 1, 'blue'),
(3, 3, 'green'),
(4, 1, 'white'),
(5, 3, 'brown'),
(6, 6, 'gray'),
(7, 3, 'black'),
(8, 1, 'orange'),
(9, 6, 'pink')
;
Query 1:
SELECT t2.color FROM mytable t1 JOIN mytable t2 ON t1.related = t2.id WHERE t1.id = '4'
Results:
| color |
|-------|
| red |
Query 2:
explain SELECT t2.color FROM mytable t1 JOIN mytable t2 ON t1.related = t2.id WHERE t1.id = '4'
Results:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|-------|------|---------------|--------|---------|--------|------|----------------------------------------------------|
| 1 | SIMPLE | t1 | ALL | (null) | (null) | (null) | (null) | 9 | Using where |
| 1 | SIMPLE | t2 | ALL | (null) | (null) | (null) | (null) | 9 | Using where; Using join buffer (Block Nested Loop) |
Query 3:
SELECT t1.color FROM mytable t1 WHERE exists (select 1 from mytable t2 where t1.id = t2.related and t2.id ='4')
Results:
| color |
|-------|
| red |
Query 4:
explain SELECT t1.color FROM mytable t1 WHERE exists (select 1 from mytable t2 where t1.id = t2.related and t2.id ='4')
Results:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|--------------------|-------|------|---------------|--------|---------|--------|------|-------------|
| 1 | PRIMARY | t1 | ALL | (null) | (null) | (null) | (null) | 9 | Using where |
| 2 | DEPENDENT SUBQUERY | t2 | ALL | (null) | (null) | (null) | (null) | 9 | Using where |
you can handle this in simple ways also
select t.color from mytable t where t.id = '$id' (for one value)
select t.color from mytable t where t.id in ('$id1','$id2','$id3','$id4' ) (for multi-values comma separated strings)

How to merge more than two (3) tables in SQL

In a Facebook-like notification system I have three tables, like this....
table1
________________________________
| id | value_1 | timestamp |
|----|-----------|-------------|
| id | value21 | 2014-05-21 |
| id | value22 | 2014-05-22 |
|____|___________|_____________|
table2
________________________________
| id | value_2 | timestamp |
|----|-----------|-------------|
| id | value30 | 2014-05-30 |
|____|___________|_____________|
table3
________________________________
| id | value_3 | timestamp |
|----|-----------|-------------|
| id | value01 | 2014-05-01 |
| id | value27 | 2014-05-27 |
|____|___________|_____________|
To return all these data in one row, I used SQL union...
(SELECT value_1 FROM table1)
UNION
(SELECT value_2 FROM table2)
UNION
(SELECT value_3 FROM table3)
...but it brings it all together in one column. Instead of that, I need the result to be in a table like this,
in order of Timestamp
________________________________________________________
| id | value_1 | value_2 | value_3 | timestamp |
|----|-----------|-----------|-----------|-------------|
| id | null | null | value01 | 2014-05-01 |
| id | value21 | null | null | 2014-05-21 |
| id | value22 | null | null | 2014-05-22 |
| id | null | null | value27 | 2014-05-27 |
| id | null | value30 | null | 2014-05-30 |
|____|___________|___________|___________|_____________|
Is it possible to do, only with SQL without handing over the job to the PHP engine?
Any ideas appreciated.
You can do this using union all and order by:
SELECT id, value_1, NULL as value_2, NULL as value_3, timestamp
FROM table1
UNION ALL
SELECT id, NULL, value_2, NULL, timestamp
FROM table2
UNION ALL
SELECT id, NULL, NULL, value_3, timestamp
FROM table3
ORDER BY timestamp;
Note that union all is more efficient than union because it does not return duplicates.
You need to add dummy columns in each part of the union:
SELECT value_1
, cast(null as <type_of_value2>) as value_2
, cast(null as <type_of_value3>) as value_3
, ts
FROM table1
UNION
SELECT cast(null as <type_of_value1>) as value_1
, value_2
, cast(null as <type_of_value3>) as value_3
, ts
FROM table2
UNION
SELECT cast(null as <type_of_value1>) as value_1
, cast(null as <type_of_value2>) as value_2
, value_3
, ts
FROM table3
ORDER BY ts

Categories