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_nameticket_name 仍然是 VARCHARS。我已将它们标记为 NOT NULL(例如:必填字段),但如果您不喜欢,可以删除该部分。 client_id/ticket_id/ticket_price 也是 NOT NULL 但改变它会产生负面影响。

ticket_price 现在是一个 DECIMAL 字段,它可以存储数字,例如 1299.5050.00 (10,2) 位意味着它涵盖了所有可能的数字,最多为 8 个整数位数 (dollars/euros/whatever) 和 2 位小数(分)。因此您可以存储从 $ -99.999.999,99 到 $ 99.999.999,99 的任何内容。 在 SQL 中总是用这种表示法写数字(比如 70k):70000.00(例如:一个点,而不是逗号;并且没有千位分隔符)。

client_idticket_id 现在都是 SERIAL,对于 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE 是 shorthand,它们都在上面 PRIMARY KEY那个。这可能听起来很复杂,但它们仍然只是普通的 INTEGERs,具有 412 等值

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 ;