MySQL select 一行 table 一秒内有多行 table 并获取 select 行中的多行数组
MySQL select row from one table with multiple rows in a second table and get array of multi row in selected row
我有一个 table 包含“客户”信息,另一个包含每个客户的“票”信息。
int-------| varchar -------| varchar
client_id | client_name | client_tickets
----------+----------------+--------------
1 | Title one | 1,2
2 | Title two | 2,3
简化票table
int--------| varchar -------| varchar
ticket_id | ticket_name | ticket_price
-----------+-------------+--------------
1 | ticketone | 30
2 | tickettwo | 40
3 | ticketthree | 50
4 | ticketfour | 60
5 | ticketfive | 70
对于以上两个 table,我想生成一个 table 和一个包含所有相关信息的查询,以生成一个搜索网格
以便给出以下输出:
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
我用的是suggest @raxi,但是我的mariadb是10.4.17-MariaDB,不支持JSON_ARRAYAGG,根据参考Creating an 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 ;
你想要什么一个相当简单的 SELECT
查询与一些 LEFT/INNER JOIN
(s)。
这个网站有一些很好的 examples/explanations 似乎非常接近您的需要:https://www.mysqltutorial.org/mysql-inner-join.aspx
我会给你一个快速的工作示例,但我不太清楚相关列的数据类型。 tables' _id
-列可能是 INTEGER 的某种变体,它们是否也是主键(或至少索引?),client_name
/ticket_name
很可能VARCHAR/TEXT/STRING 类型,但剩余的列究竟是如何存储的?作为 json 或数组或 ? (+详情)
您还用 PHP
标记了 post,您是在 SQL 查询之后吗?或寻找其中包含 SQL 的 PHP 代码。
已更新
架构的改进版本[=91=]
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
(10,2)
位意味着它涵盖了所有可能的数字,最多为 8 个整数位数 (dollars/euros/whatever) 和 2 位小数(分)。因此您可以存储从 $ -99.999.999,99 到 $ 99.999.999,99 的任何内容。
在 SQL 中总是用这种表示法写数字(比如 70k):70000.00
(例如:一个点,而不是逗号;并且没有千位分隔符)。
client_id
和 ticket_id
现在都是 SERIAL
,对于 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE
是 shorthand,它们都在上面 PRIMARY KEY
那个。这可能听起来很复杂,但它们仍然只是普通的 INTEGER
s,具有 4
或 12
等值
UNIQUE
位可以防止您拥有 2 个具有相同 ID 号的客户端,而 AUTO_INCREMENT
意味着当您添加一个新客户端时,您不必指定一个 ID(尽管您被允许去(做);你可以这样做:
INSERT INTO clients (client_name) values ('Fantastic Mr Fox');
和 client_id
将自动设置(随时间递增)。 ticket_id
在另一个 table.
中也是如此
.
我已将您原来的 client_tickets
列替换为单独的连接点 table。
那里的记录存储了客户的client_id
和属于他们的ticket_id
。
客户可以在联结 table 中有多个记录(他们拥有的每张票一个记录)。
同样,可以在任意数量的行中提及票证。
某个client_id
可能在结点table中没有任何记录。
同样,某个 ticket_id
可能在结点 table 中没有任何记录。
此 table 中不能存在相同的记录(由 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 ;
我有一个 table 包含“客户”信息,另一个包含每个客户的“票”信息。
int-------| varchar -------| varchar
client_id | client_name | client_tickets
----------+----------------+--------------
1 | Title one | 1,2
2 | Title two | 2,3
简化票table
int--------| varchar -------| varchar
ticket_id | ticket_name | ticket_price
-----------+-------------+--------------
1 | ticketone | 30
2 | tickettwo | 40
3 | ticketthree | 50
4 | ticketfour | 60
5 | ticketfive | 70
对于以上两个 table,我想生成一个 table 和一个包含所有相关信息的查询,以生成一个搜索网格 以便给出以下输出:
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
我用的是suggest @raxi,但是我的mariadb是10.4.17-MariaDB,不支持JSON_ARRAYAGG,根据参考Creating an 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 ;
你想要什么一个相当简单的 SELECT
查询与一些 LEFT/INNER JOIN
(s)。
这个网站有一些很好的 examples/explanations 似乎非常接近您的需要:https://www.mysqltutorial.org/mysql-inner-join.aspx
我会给你一个快速的工作示例,但我不太清楚相关列的数据类型。 tables' _id
-列可能是 INTEGER 的某种变体,它们是否也是主键(或至少索引?),client_name
/ticket_name
很可能VARCHAR/TEXT/STRING 类型,但剩余的列究竟是如何存储的?作为 json 或数组或 ? (+详情)
您还用 PHP
标记了 post,您是在 SQL 查询之后吗?或寻找其中包含 SQL 的 PHP 代码。
已更新
架构的改进版本[=91=]
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)
);
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
(10,2)
位意味着它涵盖了所有可能的数字,最多为 8 个整数位数 (dollars/euros/whatever) 和 2 位小数(分)。因此您可以存储从 $ -99.999.999,99 到 $ 99.999.999,99 的任何内容。
在 SQL 中总是用这种表示法写数字(比如 70k):70000.00
(例如:一个点,而不是逗号;并且没有千位分隔符)。
client_id
和 ticket_id
现在都是 SERIAL
,对于 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE
是 shorthand,它们都在上面 PRIMARY KEY
那个。这可能听起来很复杂,但它们仍然只是普通的 INTEGER
s,具有 4
或 12
等值
UNIQUE
位可以防止您拥有 2 个具有相同 ID 号的客户端,而 AUTO_INCREMENT
意味着当您添加一个新客户端时,您不必指定一个 ID(尽管您被允许去(做);你可以这样做:
INSERT INTO clients (client_name) values ('Fantastic Mr Fox');
和 client_id
将自动设置(随时间递增)。 ticket_id
在另一个 table.
.
我已将您原来的 client_tickets
列替换为单独的连接点 table。
那里的记录存储了客户的client_id
和属于他们的ticket_id
。
客户可以在联结 table 中有多个记录(他们拥有的每张票一个记录)。
同样,可以在任意数量的行中提及票证。
某个client_id
可能在结点table中没有任何记录。
同样,某个 ticket_id
可能在结点 table 中没有任何记录。
此 table 中不能存在相同的记录(由 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 ;