[英]Select multiple rows from MySQL tables for 1 user
為了便於解釋,我將嘗試簡化一切。
我有3個SQL表: Users
, Certs
, Serv
在Users
表中存儲有關唯一用戶的詳細信息。
在Certs
表中存儲有關證書的詳細信息和擁有此證書的用戶的UserId
(1個用戶可以擁有多個證書)
在Serv
表中存儲有關海上服務和用戶的UserId
的詳細信息(如Certs
表,1個用戶可以在Serv
表中有多個條目)
樣本數據
用戶
UserId Name
1 John
2 Lisa
證書
Id UserId CertName
1 1 A
2 1 B
3 1 C
4 2 A
5 2 C
SERV
UserId Name
1 SA
1 SB
1 SC
1 SD
2 S2A
我需要通過PHP檢索輸出(例如UserId = 1)同樣對於現實,每個表中會有更多列(例如證書的更多細節,如發布日期,到期日期,發布地點等):
Personal details:
Name
John
Certificates:
Certificate Id Certificate Name
1 A
2 B
3 C
Sea Services:
Sea Service Name
SA
SB
SC
SD
但是我得到錯誤的輸出,重復的條目,因為用UserId
連接2個表,其中UserId
是多個記錄的UserId
。
PHP代碼
$users = $con->prepare("
select u.Name
,GROUP_CONCAT(c.Id SEPARATOR '<br>') AS Id
,GROUP_CONCAT(c.certsName SEPARATOR '<br>') AS certsName
,GROUP_CONCAT(s.Name SEPARATOR '<br>') AS Name
from users u
left join certs c on u.UserId = c.UserId
left join serv s on u.UserId = s.UserId
where u.UserId = ?
");
$users->bind_param('i', $GetUserId);
$users->execute();
$users->bind_result(
$userName,
$certId,
$certName,
$servName
);
<?php
while ($users->fetch()) {
?>
<span>Personal Details</span>
<div class="grid-group">
<div class="grid-column">
<div class="grid-item header">User Name </div>
</div>
<div class="grid-column">
<div class="grid-item"><?php echo $userName; ?></div>
</div>
</div>
<span>Certificates</span>
<div class="grid-group">
<div class="grid-column">
<div class="grid-item header">Certificate Id</div>
<div class="grid-item header">Certificate Name</div>
</div>
<div class="grid-column">
<div class="grid-item"><?php echo $certId; ?></div>
<div class="grid-item"><?php echo $certName; ?></div>
</div>
</div>
<span>Sea Services</span>
<div class="grid-group">
<div class="grid-column">
<div class="grid-item header">Sea Service Name</div>
</div>
<div class="grid-column">
<div class="grid-item"><?php echo $servName; ?></div>
</div>
</div>
<?php } ?>
您可以檢查SQL FIDDLE以查看選擇的結果,復制行。
您是否有想法如何在不重復的情況下實現所需的輸出?
UPDATE
使用GROUP_CONCAT
與DISTINCT
后仍然是錯誤的。 想象一下,在Serv
表中,我有以下列: UserId
, Name
, Rank
和Country
如果同一個用戶在不同的公司工作(這個名稱在示例中 - 公司名稱)在不同國家/地區具有相同的級別,則選擇錯誤的數據。 例如:
Serv表(SQL)
UserId Name Rank Country
1 SA Captain USA
1 SB Captain USA
1 SC Captain RUS
1 SD Captain ENG
2 S2A Engineer USA
如果我使用這樣的查詢:
select u.Name
,GROUP_CONCAT(distinct c.Id SEPARATOR '<br>') AS Id
,GROUP_CONCAT(distinct c.certsName SEPARATOR '<br>') AS certsName
,GROUP_CONCAT(distinct s.Name SEPARATOR '<br>') AS Name
,GROUP_CONCAT(distinct s.Rank SEPARATOR '<br>') AS Rank
,GROUP_CONCAT(distinct s.Country SEPARATOR '<br>') AS Country
from users u
left join certs c on u.UserId = c.UserId
left join serv s on u.UserId = s.UserId
where u.UserId = ?
所以GROUP_CONCAT(DISTINCT..)
讓我GROUP_CONCAT(DISTINCT..)
如下:
......
Sea Services:
Sea Service Name Rank Country
SA Captain USA
SB RUS
SC ENG
SD
只有第一行有排名,前三行有國家/地區返回,但在數據庫中存儲了每行的排名和國家/地區。
使用此數據的完整所需輸出應如下所示:
Personal details:
Name
John
Certificates:
Certificate Id Certificate Name
1 A
2 B
3 C
Sea Services:
Sea Service Name Rank Country
SA Captain USA
SB Captain USA
SC Captain RUS
SD Captain ENG
你可以在SQL FIDDLE上查看它
更新2
如果我刪除DISTINCT
我得到以下輸出:
Sea Service Name Rank Country
SA Captain USA
SA Captain USA
SA Captain USA
SB Captain USA
SB Captain USA
SB Captain USA
SC Captain RUS
SC Captain RUS
SC Captain RUS
SD Captain ENG
SD Captain ENG
SD Captain ENG
如果我正在使用DISTINCT
我會這樣:
Sea Services:
Sea Service Name Rank Country
SA Captain USA
SB RUS
SC ENG
SD
但它應該是:
Sea Services:
Sea Service Name Rank Country
SA Captain USA
SB Captain USA
SC Captain RUS
SD Captain ENG
更新3
想象一下,我有固定的列寬,我有很長的Sea Service Name,它將被包裝到新行:
Sea Service Name | Rank | Country
-----------------|--------|---------
This is long Sea | Captain| USA
Service Name |--------|---------
-----------------| Captain| RUS
Other Name |--------|---------
-----------------| Captain| ENG
Another long Sea |--------|---------
Service Name | Master | USA
-----------------|--------|---------
Other Sea Serv |
-----------------|
如您所見,每列都是獨立的,行不匹配。 但它應該像1排。 所以我覺得我用GROUP_CONCAT
無法實現它,看起來我需要另一種方式。
它看起來如何:
您缺少group by
子句:
select u.Name
,GROUP_CONCAT(distinct c.Id SEPARATOR '<br>') AS Id
,GROUP_CONCAT(distinct c.certsName SEPARATOR '<br>') AS certsName
,GROUP_CONCAT(distinct s.Name SEPARATOR '<br>') AS Name
,(SELECT GROUP_CONCAT(ss.Rank SEPARATOR '<br>') FROM users uu
LEFT OUTER JOIN serv ss ON (uu.UserId = ss.UserId)
WHERE uu.user_id = u.user_id) as Rank
,GROUP_CONCAT(distinct s.Country SEPARATOR '<br>') AS Country
from users u
left join certs c on u.UserId = c.UserId
left join serv s on u.UserId = s.UserId
where u.UserId = ?
此外,我已經為您的GROUP_CONCAT
添加了不同之處,因為您要為每個用戶連接多個行的多個表,您將有多個重復項。
這看起來像是為了避免多次查詢而付出的努力。
把事情簡單化
您可以運行查詢:
SELECT ...
FROM users u
LEFT JOIN certs c on u.UserId = c.UserId
LEFT JOIN serv s on u.UserId = s.UserId
WHERE u.UserId = ?
並在應用程序邏輯中分離證書和服務。
或者只運行兩到三個單獨的查詢:
SELECT * FROM users u WHERE u.UserId = ?
SELECT * FROM certs c WHERE c.UserId = ?
SELECT * FROM serv s WHERE s.UserId = ?
雖然有3個查詢的開銷,但正確的索引將會很快瘋狂,並且您減少了冗余數據轉換的數量。
這些簡單的查詢很容易調試和理解。 您擁有的查詢對於此操作非常復雜,即使是微小的更改也會導致問題。
另外,請從數據庫中分離出格式。 如果我在布局中遇到間距問題,我看到的最后一個地方就是數據庫查詢。
保持應用程序的單獨層分開允許您一次處理一個問題並更改數據的顯示而不必擔心數據本身。
在使用重復條目獲取整個數據並在PHP中分離它們之后,可以使用應用程序邏輯簡單地完成此操作。 永遠記住,您可以擁有多個應用程序服務器來分配其負載,但您總是希望擁有單個數據庫服務器,您需要最少的查詢負載。 此外,客戶負載可以在應用程序服務器之間分配,其中相同數量的客戶端將訪問相同的數據庫。
您需要將DISTINCT
添加到GROUP_CONCAT
因為您從certs
連接n行到來自serv
m行,導致n * m行包含大量重復項。
但是,當你做你的GROUP_CONCAT
使用派生表你會得到每個表只能有一行的每個表:
select u.Name
,c.Id
,c.certsName
,s.Name
,s.Rank
,s.Country
from users u
left join
( select UserId
,GROUP_CONCAT(Id SEPARATOR '<br>') AS Id
,GROUP_CONCAT(certsName SEPARATOR '<br>') AS certsName
from certs
where UserId = 1
group by UserId
) as c on u.UserId = c.UserId
left join
( select UserId
,GROUP_CONCAT(Name SEPARATOR '<br>') AS Name
,GROUP_CONCAT(Rank SEPARATOR '<br>') AS Rank
,GROUP_CONCAT(Country SEPARATOR '<br>') AS Country
from serv
where UserId = 1
group by UserId
) as s on u.UserId = s.UserId
where u.UserId = 1
見小提琴
當然,提交單獨查詢的建議仍然是最佳答案,其他一切只是一種解決方案。
SELECT
u.name,
(SELECT GROUP_CONCAT(c.id SEPARATOR '<br>') FROM certs c WHERE c.userId = u.userId) AS Id,
(SELECT GROUP_CONCAT(c.CertsName SEPARATOR '<br>') FROM certs c WHERE c.userId = u.userId) AS certsName,
(SELECT GROUP_CONCAT(s.Name SEPARATOR '<br>') FROM serv s WHERE s.userId = u.userId) AS Name
FROM users u WHERE u.userId = ?
從TABLE1(TB1)和TABLE2(TB2)中選擇多行:來自TABLE1的CM1_1,CM1_2和來自TABLE2的CM2_1,CM2_2,CM2_3
SELECT CM1_1 , CM1_2 , CM2_1 , CM2_2 , CM2_3 FROM TB1, TB2 WHERE CM1_2 = '1' AND CM1_1 = CM2_1 AND CM2_3 = 'John'
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.