如何仅在内连接查询的第二个表中选择第一行

时间:2015-07-16 09:16:27

标签: php mysql

我正在使用以下查询从3个不同的表中选择数据。 tbl_invoices和tbl_clients具有唯一记录。每个tbl_invoices记录都有多个tbl_invoice_entries记录:

$HOME/.bashrc

这将返回tbl_invoice_entries中的所有记录。如何更改查询以仅返回每个tbl_invoices记录的第一个tbl_invoice_entries记录。

以下是表格:

tbl_clients

$query = 'SELECT T1.*, T2.*, T3.* 
                FROM tbl_invoices T1 
                LEFT JOIN tbl_invoice_entries T2
                ON T1.number = T2.invoice_number
                LEFT JOIN tbl_clients T3
                ON T1.client = T3.client_id
                WHERE date_format(date, '%Y') = ".$_POST['year']." AND date_format(date, '%c') = ".$_POST['month']." ORDER BY date, number ASC'

$stmt = $conn->prepare($query)
$stmt->execute();    
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

tbl_invoices

+----+-----------+----------+
| id | firstname | lastname |
+----+-----------+----------+
|  1 | John      | Doe      |
|  2 | Jane      | Doe      |
+----+-----------+----------+

tbl_invoice_entries

+----+--------+--------+------------+
| id | number | client |    date    |
+----+--------+--------+------------+
|  1 |     14 |      1 | 2015-07-14 |
|  1 |     15 |      2 | 2015-07-14 |
+----+--------+--------+------------+

所以我正在寻找的结果是:

John Doe 14 Fish 2015-07-14

Jane Doe 15蔬菜2015-07-14

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

通过将invoice_entries表直接链接到发票编号而不是第一个条目的ID,您可以实现您想要的目标:

SELECT firstname,lastname,number,product,date
 FROM tbl_invoices T1 
 LEFT JOIN tbl_invoice_entries T2
 ON T2.id =(select min(id) from tbl_invoice_entries 
            where invoice_number=number)
 LEFT JOIN tbl_clients T3
 ON T1.client = T3.id
 WHERE ...

答案 1 :(得分:1)

您需要通过第一行告诉RDBMS您的意图。元组中没有自然顺序。如果你想要给出相同invoice_number 的最低ID的元组,那么它将需要另一个查询

SELECT tbl1.* FROM tbl_invoice_entries AS tbl1
    JOIN ( SELECT MIN(id) AS id, invoice_number FROM tbl_invoice_entries
         GROUP BY invoice_number ) AS tbl2
    USING (id);

上述查询相当于tbl_invoice_entries,但只有每个发票编号的最低ID。您可以将其作为VIEW(实际上是两个,因为您不能在VIEW中使用子查询):

CREATE VIEW tbl_invoice_entries_firstnumber AS
  SELECT MIN(id) AS id, invoice_number
  FROM tbl_invoice_entries
  GROUP BY invoice_number;

CREATE VIEW tbl_invoice_entries_first AS
  SELECT tbl1.* FROM tbl_invoice_entries AS tbl1
  JOIN tbl_invoice_entries_firstnumber
  USING (id);

之后,您可以在当前查询中使用tbl_invoice_entries_first代替tbl_invoice_entries

请注意,视图是动态,因此它只是更复杂查询的简写。这意味着您当前的查询将变得更加复杂,需要更长的时间:

SELECT T1.*, T2.*, T3.* 
    FROM tbl_invoices AS T1 
    LEFT JOIN tbl_invoice_entries_first AS T2
        ON T1.number = T2.invoice_number
    LEFT JOIN tbl_clients AS T3
        ON T1.client = T3.id; -- you have no client_id in T3

我已经设置了一个小提琴here

或者你可以更多地修改你的查询,并在T2上添加一个JOIN条件,这样它只会再次获取最小ID - 或者你喜欢的任何排序条件:

SELECT T1.*, T2.*, T3.* 
    FROM tbl_invoices AS T1 
    LEFT JOIN tbl_invoice_entries AS T2
        ON (
          -- (( T1.number = T2.invoice_number AND  )) --
          T2.id = (
          SELECT MIN(id) FROM tbl_invoice_entries 
          WHERE invoice_number = number
        ))
    LEFT JOIN tbl_clients AS T3
        ON T1.client = T3.id;

更新对数字的检查已被注释掉(另请参阅@ cars10的解决方案),因为它由内部子查询继承。

最后,您可以在代码中执行此操作,即保存上一个元组的值并根据需要对查询进行排序;然后丢弃所有不需要的元组。如果每张发票的条目很少,这可能是值得的:

 // pseudo code

 if (prev.client == tuple.client)
    and
    (prev.invoice == tuple.invoice)
        continue;
 prev = tuple;
 -- use tuple.