我使用下面的查询从3个不同的表中选择数据。Tbl_invoices和tbl_clients有唯一的记录。每个tbl_invoices记录有多个tbl_invoice_entries记录:
$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_invoice_entries中的所有记录。如何更改查询以仅返回每个tbl_invoices记录的第一个tbl_invoice_entries记录?
表格如下:
tbl_clients
+----+-----------+----------+
| id | firstname | lastname |
+----+-----------+----------+
| 1 | John | Doe |
| 2 | Jane | Doe |
+----+-----------+----------+
tbl_invoices
+----+--------+--------+------------+
| id | number | client | date |
+----+--------+--------+------------+
| 1 | 14 | 1 | 2015-07-14 |
| 1 | 15 | 2 | 2015-07-14 |
+----+--------+--------+------------+
tbl_invoice_entries
+----+----------------+------------+
| id | invoice_number | produkt |
+----+----------------+------------+
| 1 | 14 | Fish |
| 2 | 14 | Bread |
| 3 | 15 | Vegetables |
| 4 | 15 | Fruit |
+----+----------------+------------+
所以我要找的结果是:
约翰·多伊14鱼2015-07-14
简·多伊15蔬菜2015-07-14
谢谢你的帮助!
通过链接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 ...
您需要通过第一行告诉RDBMS您的意图。元组中没有自然顺序。如果您希望具有最低ID的元组具有相同的invoice_number,则需要另一个查询
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
我在这里放了一把小提琴。
或者您可以更多地修改您的查询,并在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;
UPDATE: number检查被注释掉了(参见@cars10的解决方案),因为它被内部子查询结转。
最后你可以在代码中做到这一点,即你保存前一个元组的值并根据需要排序查询;然后丢弃所有不需要的元组。如果每张发票的条目很少,那么这可能是值得的:
// pseudo code
if (prev.client == tuple.client)
and
(prev.invoice == tuple.invoice)
continue;
prev = tuple;
-- use tuple.