MySQL从一个表中选择行,第二个表中有多行,并在所选行中获取多行数组
Posted
技术标签:
【中文标题】MySQL从一个表中选择行,第二个表中有多行,并在所选行中获取多行数组【英文标题】:MySQL select row from one table with multiple rows in a second table and get array of multi row in selected row 【发布时间】:2022-01-16 13:52:20 【问题描述】:我有一个包含“客户”信息的表格,另一个包含每个客户的“票证”信息。
int-------| varchar -------| varchar
client_id | client_name | client_tickets
----------+----------------+--------------
1 | Title one | 1,2
2 | Title two | 2,3
简化票务表
int--------| varchar -------| varchar
ticket_id | ticket_name | ticket_price
-----------+-------------+--------------
1 | ticketone | 30
2 | tickettwo | 40
3 | ticketthree | 50
4 | ticketfour | 60
5 | ticketfive | 70
使用上述两个表,我想生成一个带有单个查询的单个表,其中包含所有相关信息以生成搜索网格 从而给出以下输出:
client_id | client_name | client_tickets | ticket_names | ticket_prices
----------+----------------+----------------+-----------------------+--
1 | Title one | 1,2 | ticketone,tickettwo | 30,40
2 | Title two | 2,3 | tickettwo,ticketthree | 40,50
ticket_names,ticket_ids,client_name 是 varchar
我想通过一个请求接收最后 5 列 例如:
SELECT s.*,
(SELECT GROUP_CONCAT(ticket_name SEPARATOR ',') FROM tickets_table WHERE ticket_id IN(s.client_tickets)) AS ticket_names,
(SELECT GROUP_CONCAT(ticket_price SEPARATOR ',') FROM tickets_table WHERE ticket_id IN(s.client_tickets)) AS ticket_prices
FROM client_table s where s.client_id=1
这似乎有问题 你有更好的建议吗?
请提出您的建议
更新: 清理我想要的结果 以下代码有两个查询, 我希望这段代码通过查询来完成
$client_result = $conn->query("SELECT * FROM client_table where client_id=1");
while($client_row = $client_result->fetch_assoc())
$ticket_result = $conn->query("SELECT * FROM tickets_table where ticket_id IN ($client_row['client_tickets'])");
while($ticket_row = ticket_result->fetch_assoc())
echo $ticket_row['ticket_name']."<br>";
更新 2
我使用建议 @raxi ,但我的 mariadb 是 10.4.17-MariaDB 并且不支持 JSON_ARRAYAGG ,根据参考创建 aggregate function 解决它 , 使用 SQL
DELIMITER //
DROP FUNCTION IF EXISTS JSON_ARRAYAGG//
CREATE AGGREGATE FUNCTION IF NOT EXISTS JSON_ARRAYAGG(next_value TEXT) RETURNS TEXT
BEGIN
DECLARE json TEXT DEFAULT '[""]';
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN json_remove(json, '$[0]');
LOOP
FETCH GROUP NEXT ROW;
SET json = json_array_append(json, '$', next_value);
END LOOP;
END //
DELIMITER ;
【问题讨论】:
你想保持表格原样吗?或者您是否希望看到一个能让事情变得更容易的架构(两种情况下的连接查询完全不同) 不,如果有更好的办法,那我就去做 【参考方案1】:您想要一个相当简单的 SELECT
查询和一些 LEFT/INNER JOIN
(s)。
这个网站有一些很好的例子/解释似乎非常接近您的需要:https://www.mysqltutorial.org/mysql-inner-join.aspx
我会给你一个简单的例子,但我并不清楚相关列是什么数据类型。两个表的_id
-columns 可能是 INTEGER 的一些变体,它们是否也是主键(或至少索引?),client_name
/ticket_name
可能是 VARCHAR/TEXT/STRING 类型,但究竟是剩余的列存储?作为 json 或数组或? (+细节)
您还用php
标记了您的帖子,您是在SQL 查询之后吗?或寻找其中包含 SQL 的 PHP 代码。
更新
架构的改进版本
CREATE TABLE clients (
client_id SERIAL,
client_name VARCHAR(255) NOT NULL,
PRIMARY KEY (client_id)
);
CREATE TABLE tickets (
ticket_id SERIAL,
ticket_name VARCHAR(255) NOT NULL,
ticket_price DECIMAL(10,2) NOT NULL,
PRIMARY KEY (ticket_id)
);
-- A junction table to glue those 2 tables together (N to N relationship)
CREATE TABLE client_tickets (
client_id BIGINT UNSIGNED NOT NULL,
ticket_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (client_id, ticket_id)
);
我已更改数据类型。
client_name
和 ticket_name
仍然是 VARCHARS。我已将它们标记为NOT NULL
(例如:必填字段),但如果您不喜欢,可以删除该部分。
client_id
/ticket_id
/ticket_price
也是 NOT NULL
,但更改会产生负面影响。
ticket_price
现在是一个 DECIMAL 字段,可以存储诸如 1299.50
或 50.00
之类的数字2 位小数(美分)。所以你可以存储从 $ -99.999.999,99 到 $ 99.999.999,99 的任何东西。
在 SQL 中,总是用这种表示法写数字(比如 70k):70000.00
(例如:点,而不是逗号;并且没有千位运算符)。
client_id
和ticket_id
现在都是SERIAL
s,这是BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE
的简写,并且它们都是PRIMARY KEY
s。这听起来可能很复杂,但它们仍然只是普通的INTEGER
s,具有4
或12
等值。
UNIQUE
位防止您拥有 2 个具有相同 ID 号的客户端,AUTO_INCREMENT
意味着当您添加新客户端时,您不必指定 ID(尽管您可以这样做);你可以这样做:
INSERT INTO clients (client_name) values ('Fantastic Mr Fox');
client_id
将自动设置(随时间递增)。另一个表中的ticket_id
也是如此。
.
我已将您原来的 client_tickets
列替换为单独的连接表。
那里的记录存储客户的client_id
和属于他们的ticket_id
。
一个客户端可以在联结表中有多个记录(他们拥有的每张票一个记录)。
同样,可以在任意数量的行上提及一张票。
某个client_id
可能在联结表中没有任何记录。
同样,某个ticket_id
可能在联结表中没有任何记录。
此表中不能存在相同的记录(由PRIMARY KEY
强制执行)。
测试数据
接下来,我们可以放一些数据来测试它:
-- Create some tickets
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (1, 'ticketone', '30' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (2, 'tickettwo', '40' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (3, 'ticketthree', '50' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (4, 'ticketfour', '60' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (5, 'ticketfive', '70' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (6, 'ticketsix', '4' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (7, 'ticketseven', '9' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (8, 'ticketeight', '500' );
-- Create some users, and link them to some of these tickets
INSERT INTO clients (client_id, client_name) values (1, 'John');
INSERT INTO client_tickets (client_id, ticket_id) values (1, 3);
INSERT INTO client_tickets (client_id, ticket_id) values (1, 7);
INSERT INTO client_tickets (client_id, ticket_id) values (1, 1);
INSERT INTO clients (client_id, client_name) values (2, 'Peter');
INSERT INTO client_tickets (client_id, ticket_id) values (2, 5);
INSERT INTO client_tickets (client_id, ticket_id) values (2, 2);
INSERT INTO client_tickets (client_id, ticket_id) values (2, 3);
INSERT INTO clients (client_id, client_name) values (3, 'Eddie');
INSERT INTO client_tickets (client_id, ticket_id) values (3, 8);
INSERT INTO clients (client_id, client_name) values (9, 'Fred');
-- Note: ticket #3 is owned by both client #1/#2;
-- Note: ticket #4 and #6 are unused;
-- Note: client #9 (Fred) has no tickets;
查询
获取所有现有关系(无票客户被排除在外,无所有者票被排除在外)
SELECT clients.*
, tickets.*
FROM client_tickets AS ct
INNER JOIN clients ON ct.client_id = clients.client_id
INNER JOIN tickets ON ct.ticket_id = tickets.ticket_id
ORDER BY clients.client_id ASC
, tickets.ticket_id ASC ;
获取所有仍然免费的门票(无所有者)
SELECT tickets.*
FROM tickets
WHERE tickets.ticket_id NOT IN (
SELECT ct.ticket_id
FROM client_tickets AS ct
)
ORDER BY tickets.ticket_id ASC ;
获取所有客户(甚至是无票客户)的列表,并包括每个客户有多少票以及他们的票的总价格。
SELECT clients.*
, COALESCE(COUNT(tickets.ticket_id), 0) AS amount_of_tickets
, COALESCE(SUM(tickets.ticket_price), 0.00) AS total_price
FROM clients
LEFT JOIN client_tickets AS ct ON ct.client_id = clients.client_id
LEFT JOIN tickets ON ct.ticket_id = tickets.ticket_id
GROUP BY clients.client_id
ORDER BY clients.client_id ASC ;
将所有有趣的信息放在一起(不包括无所有者门票)
SELECT clients.*
, COALESCE(COUNT(sub.ticket_id), 0) AS amount_of_tickets
, COALESCE(SUM(sub.ticket_price), 0.00) AS total_price
, JSON_ARRAYAGG(sub.js_tickets_row) AS js_tickets_rows
FROM clients
LEFT JOIN client_tickets AS ct ON ct.client_id = clients.client_id
LEFT JOIN (
SELECT tickets.*
, JSON_OBJECT( 'ticket_id', tickets.ticket_id
, 'ticket_name', tickets.ticket_name
, 'ticket_price', tickets.ticket_price
) AS js_tickets_row
FROM tickets
) AS sub ON ct.ticket_id = sub.ticket_id
GROUP BY clients.client_id
ORDER BY clients.client_id ASC ;
-- sidenote: output column `js_tickets_rows` (a json array) may contain NULL values
包含一些汇总数据的所有票证列表
SELECT tickets.*
, IF(COALESCE(COUNT(clients.client_id), 0) > 0
, TRUE, FALSE) AS active
, COALESCE( COUNT(clients.client_id), 0) AS amount_of_clients
, IF(COALESCE( COUNT(clients.client_id), 0) > 0
, GROUP_CONCAT(clients.client_name SEPARATOR ', ')
, NULL) AS client_names
FROM tickets
LEFT JOIN client_tickets AS ct ON ct.ticket_id = tickets.ticket_id
LEFT JOIN clients ON ct.client_id = clients.client_id
GROUP BY tickets.ticket_id
ORDER BY tickets.ticket_id ASC
, clients.client_id ASC ;
【讨论】:
问题已更新..请检查一下, 谢谢,但值 client_tickets = (1,2) 表示 (ticket_id IN (1,2)) OR 表示 (ticket_id=1 OR ticket_id=2 ) 逗号是门票 ID 的分隔符 根据你的回答,必须为每个client_id创建一个ticket,但是我们想使用一些在client_tickets列中用逗号分隔的预制ticket 我更新了问题,所需的输出是用两个查询编写的,但我需要用一个查询来获得这个输出 嗯,我明白了,是的,我当时并没有完全理解门票的想法,但在这种情况下,是否有可能说门票 6 适用于超过 1 个客户? (我假设没有?)它真的总是2张票吗? (从不 1 或 3 或 4 等)——这将是一个简单的改变;如果您能稍微澄清一下这些票证,我会更新示例;以上是关于MySQL从一个表中选择行,第二个表中有多行,并在所选行中获取多行数组的主要内容,如果未能解决你的问题,请参考以下文章
MYSQL:我想用另一个表中的值更新表中的所有行,其中第一个表中的值等于第二个表