mysql - 多对多加入当前拥有的行
问题描述
本质上是尝试为位置索引视图编写 MySQL 查询(可以是任何版本),该视图显示与位置具有当前关联的所有项目。item_location 表代表项目移动的位置和时间。
items
______________
| id | name |
--------------
| 1 | item1 |
| 2 | item2 |
| 3 | item3 |
| 4 | item4 |
--------------
locations
______________
| id | city |
--------------
| 1 | city1 |
| 2 | city2 |
| 3 | city3 |
| 4 | city4 |
--------------
item_location
_______________________________________________________
| id | item_id | location_id | date |
------------------------------------------------------|
| 1 | 1 | 1 | 1998-01-01 04:00:00 |
| 2 | 1 | 2 | 1998-01-02 04:00:00 |
| 3 | 2 | 1 | 1998-01-03 04:00:00 |
| 4 | 3 | 1 | 1998-01-04 04:00:00 |
| 4 | 4 | 3 | 1999-01-04 04:00:00 |
| 4 | 4 | 4 | 1999-02-04 04:00:00 |
-------------------------------------------------------
expected output (with limit 3):
____________________________________________________________________________________________
| location.id | location.city | items |
--------------------------------------------------------------------------------------------
| 1 | city1 | [{"id":"2", "name": "item2"}, {"id":"3", "name": "item3"}] |
| 2 | city2 | [{"id":"1", "name": "item1"}] |
| 3 | city3 | [], '', or null because city4 has latest date. |
--------------------------------------------------------------------------------------------
如果在这种情况下有更常见或更有效的做法,我愿意重组我的数据库。
我一直在使用 JSON_ARRAYAGG(JSON_OBJECT("id", item.id, "name", item.name)) 来构建 json 对象的数组。如果也有更好/通用/性能更好的替代方案,我很开放。
我已经为我的情况创建了一个可重现的可运行示例。
架构(MySQL v5.7)
CREATE TABLE `item_location` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`location_id` int unsigned NOT NULL,
`item_id` int unsigned NOT NULL,
`date` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `location_id` (`location_id`),
KEY `machine_id` (`item_id`)
) ENGINE=InnoDB;
CREATE TABLE `locations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`city` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `items` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(25) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `items` (`id`, `name`) VALUES
(1, '1405'), (2, '1447'), (3, '1641');
INSERT INTO `locations` (`id`, `city`) VALUES
(1, 'Kingsville'), (2, '
Wright City'), (3, 'Racine');
INSERT INTO `item_location` (`id`, `location_id`, `item_id`, `date`) VALUES
(1, 3, 1, '1992-01-14 05:00:00'),
(2, 3, 2, '1993-02-05 05:00:00'),
(3, 3, 3, '1997-05-13 04:00:00'),
(4, 1, 3, '2018-01-15 05:00:00'),
(5, 2, 3, '2006-07-01 04:00:00')
尝试的查询 #1
SELECT DISTINCT item_location.location_id, locations.city, date, JSON_ARRAYAGG(JSON_OBJECT("id", items.id, "name", items.name)) AS items
FROM item_location
INNER JOIN items ON item_location.item_id = items.id
INNER JOIN locations ON item_location.location_id = locations.id
GROUP BY items.name, item_location.location_id, date;
| location_id | date | city | items |
| ----------- | ------------------- | ------------ | --------------------------- |
| 3 | 1992-01-14 05:00:00 | Racine | [{"id": 1, "name": "1405"}] |
| 3 | 1993-02-05 05:00:00 | Racine | [{"id": 2, "name": "1447"}] |
| 1 | 2018-01-15 05:00:00 | Kingsville | [{"id": 3, "name": "1641"}] |
| 2 | 2006-07-01 04:00:00 | Wright City | [{"id": 3, "name": "1641"}] |
| 3 | 1997-05-13 04:00:00 | Racine | [{"id": 3, "name": "1641"}] |
---
预期结果:
| location_id | city | items |
| ----------- | ------------ | ———————————————————————————————————————————————————————— |
| 3 | Racine | [{"id": "1", "name": "1405"}, {"id": "2", "name": "1447"}] |
| 1 | Kingsville | [{"id": "3", "name": "1641"}] |
| 2 | Wright City | [] |
这是我最接近的查询,除了我不确定 MySQL 是否有办法能够知道哪个项目是该位置的最新所有者:
查询 #1
select `locations`.`id`, `locations`.`city` as `city`, `itms`.`items` from locations
left join (
SELECT il.location_id AS id, JSON_ARRAYAGG(JSON_OBJECT("name", i.name)) AS items
FROM item_location il
JOIN items i ON i.id = il.item_id
GROUP BY il.location_id
) itms USING (id)
order by `city`;
| id | city | items |
| --- | ------------ | ------------------------------------------------------ |
| 2 | Wright City | [{"name": "1641"}] |
| 1 | Kingsville | [{"name": "1641"}] |
| 3 | Racine | [{"name": "1405"}, {"name": "1447"}, {"name": "1641"}] |
解决方案
SELECT DISTINCT item_location.location_id, locations.city, JSON_ARRAYAGG(JSON_OBJECT("id", items.id, "name", items.name)) AS items
FROM item_location
INNER JOIN items ON item_location.item_id = items.id
INNER JOIN locations ON item_location.location_id = locations.id
GROUP BY items.name
您可以根据需要将日期添加到上述查询中。
您的问题的答案将是以下是需要改进的领域。了解 MySQL EXPLAIN
。阅读更多。
EXPLAIN SELECT DISTINCT item_location.location_id, locations.city, JSON_ARRAYAGG(JSON_OBJECT("id", items.id, "name", items.name)) AS items FROM item_location INNER JOIN items ON item_location.item_id = items.id INNER JOIN locations ON item_location.location_id = locations.id GROUP BY items.name
您需要学习如何删除 Using temporary;使用文件排序等。互联网上有大量教程可供学习。
如果您需要进一步的帮助,请告诉我。
推荐阅读
- javascript - 一些 CSS 文件需要“text/css”,而一些需要“stylesheet/css”类型
- python - 如何使用 BeautifulSoup 从特定跨度中提取文本?
- linux - 如何在 Docker 容器中挂载 /proc
- javascript - lua http.get body 获取 php 文件,返回站点代码而不是正文
- linq - 你如何在 Linq 中写一个带有“OR”的 INNER JOIN
- flutter - 坏状态:没有元素让电话号码列出
- javascript - 回调不是异步库中的函数
- visual-studio - 如何定位具有相同距离的对象
- ios - TableView Cell Swift 中的 CollectionView 混合卡片类型重用问题
- angularjs - 在 AngularJS 中找不到使用 CSS 选择器或 xPath 的元素