簡體   English   中英

從MySQL表中為1個用戶選擇多行

[英]Select multiple rows from MySQL tables for 1 user

為了便於解釋,我將嘗試簡化一切。

我有3個SQL表: UsersCertsServ

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_CONCATDISTINCT后仍然是錯誤的。 想象一下,在Serv表中,我有以下列: UserIdNameRankCountry

如果同一個用戶在不同的公司工作(這個名稱在示例中 - 公司名稱)在不同國家/地區具有相同的級別,則選擇錯誤的數據。 例如:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM