2010-11-17 43 views
7

我認爲自己在理解和操作C-ish語言方面相當有能力;我想出一個算法並用任何C語言實現它都不是問題。什麼是構建MySQL查詢的結構化方式?

編寫SQL(在我的具體情況下,MySQL)查詢有很大的困難。對於非常簡單的查詢,這不是問題,但對於複雜的查詢,我會感到沮喪,不知道從哪裏開始。閱讀MySQL文檔是很困難的,主要是因爲語法描述和解釋沒有很好地組織。

例如,SELECT文檔是各地圖:它開始時的看上去像是僞BNF,但之後(因爲骨料的說明文字是不能點擊...喜歡select_expr)很快的不滿轉化這種嘗試通過打開許多瀏覽器窗口來自定義語法的令人沮喪的練習。

足夠的嗚嗚聲。

我想知道人們如何逐步開始構建複雜的MySQL查詢。這裏是一個具體的例子。下面有三張表。我想SELECT一組行具有以下特點:

userInfouserProgram表,我想選擇userNameisApprovedmodifiedTimestamp領域和UNION他們到一組。從這組數據中,我想要ORDER,modifiedTimestamp對於每個用戶取MAX(modifiedTimestamp)(即,應該只有一行具有唯一的userName,並且與該用戶名相關聯的時間戳應該儘可能高)。

user表,我想匹配的firstName並與該userName,使它看起來像這樣有關lastName

+-----------+----------+----------+-------------------+ 
| firstName | lastName | userName | modifiedTimestamp | 
+-----------+----------+----------+-------------------+ 
| JJ  | Prof  | jjprofUs |  1289914725 | 
| User  | 2  | user2 |  1289914722 | 
| User  | 1  | user1 |  1289914716 | 
| User  | 3  | user3 |  1289914713 | 
| User  | 4  | user4 |  1289914712 | 
| User  | 5  | user5 |  1289914711 | 
+-----------+----------+----------+-------------------+ 

最近我有一個查詢,看起來像這樣的:

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userInfo 
WHERE user.userName=userInfo.userName) 

UNION 

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userProgram 
WHERE user.userName=userProgram.userName) 

ORDER BY modifiedTimestamp DESC; 

我覺得我很接近,但我不知道在哪裏可以從這裏走,甚至如果我在正確的方式思考這個。

> user 
+--------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+--------------------+--------------+------+-----+---------+-------+ 
| userName   | char(8)  | NO | PRI | NULL |  | 
| firstName   | varchar(255) | NO |  | NULL |  | 
| lastName   | varchar(255) | NO |  | NULL |  | 
| email    | varchar(255) | NO | UNI | NULL |  | 
| avatar    | varchar(255) | YES |  | ''  |  | 
| password   | varchar(255) | NO |  | NULL |  | 
| passwordHint  | text   | YES |  | NULL |  | 
| access    | int(11)  | NO |  | 1  |  | 
| lastLoginTimestamp | int(11)  | NO |  | -1  |  | 
| isActive   | tinyint(4) | NO |  | 1  |  | 
+--------------------+--------------+------+-----+---------+-------+ 

> userInfo 
+-------------------+------------+------+-----+---------+-------+ 
| Field    | Type  | Null | Key | Default | Extra | 
+-------------------+------------+------+-----+---------+-------+ 
| userName   | char(8) | NO | MUL | NULL |  | 
| isApproved  | tinyint(4) | NO |  | 0  |  | 
| modifiedTimestamp | int(11) | NO |  | NULL |  | 
| field    | char(255) | YES |  | NULL |  | 
| value    | text  | YES |  | NULL |  | 
+-------------------+------------+------+-----+---------+-------+ 

> userProgram 
+-------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+-------------------+--------------+------+-----+---------+-------+ 
| userName   | char(8)  | NO | PRI | NULL |  | 
| isApproved  | tinyint(4) | NO | PRI | 0  |  | 
| modifiedTimestamp | int(11)  | NO |  | NULL |  | 
| name    | varchar(255) | YES |  | NULL |  | 
| address1   | varchar(255) | YES |  | NULL |  | 
| address2   | varchar(255) | YES |  | NULL |  | 
| city    | varchar(50) | YES |  | NULL |  | 
| state    | char(2)  | YES | MUL | NULL |  | 
| zip    | char(10)  | YES |  | NULL |  | 
| phone    | varchar(25) | YES |  | NULL |  | 
| fax    | varchar(25) | YES |  | NULL |  | 
| ehsChildren  | int(11)  | YES |  | NULL |  | 
| hsChildren  | int(11)  | YES |  | NULL |  | 
| siteCount   | int(11)  | YES |  | NULL |  | 
| staffCount  | int(11)  | YES |  | NULL |  | 
| grantee   | varchar(255) | YES |  | NULL |  | 
| programType  | varchar(255) | YES |  | NULL |  | 
| additional  | text   | YES |  | NULL |  | 
+-------------------+--------------+------+-----+---------+-------+ 
+1

這與jQuery完全無關。已移除標記。 – casablanca 2010-11-17 00:28:28

+0

如果您想學習SQL,使用它的數據庫實現文檔不是很好的文獻。該文檔是爲已經熟悉SQL的人編寫的。所以,你應該找一些學習SQL的文獻來代替。 – Guffa 2010-11-17 00:57:15

回答

1

對於我從你的問題明白了,你似乎需要一個相關的查詢,這將是這樣的:

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userInfo ui1 
WHERE user.userName=userInfo.userName 
AND modifiedtimestamp=(select max(modifiedtimestamp) from userInfo ui2 where ui1.userName=ui2.userName)) 

UNION 

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userProgram up1 
WHERE user.userName=userProgram.userName 
AND modifiedtimestamp=(select max(modifiedtimestamp) from userProgram up2 where up1.userName=up2.userName)) 
ORDER BY modifiedTimestamp DESC; 

這樣,我繼續得到這樣的結果?關鍵是:明確表達您想要檢索的信息,而不需要採取精神捷徑。

第1步:選擇我需要在我的數據庫的不同表中的字段。這就是SELECT和FROM之間的內容。看起來很明顯,但它涉及聚合函數(如總和或計數)時變得不那麼明顯。在這種情況下,你必須說,例如「我需要每個firstName的userInfo中的行數」。請參閱下面的GROUP BY。

第2步:瞭解您需要的字段,編寫不同的對應表之間的連接。這是一個容易的...

第3步:表達你的條件。它可以很容易,就像你希望userName =「RZEZDFGBH」的用戶數據一樣,或者更復雜一些,比如你的情況:制定它的方式,這樣你可以完成任務,如果你只想要最新的修改時間戳,是「使該modifiedtimestamp等於最近modifiedtimestamp」(這就是你可以很容易地採取心理捷徑,錯過了這一點)

第4步:如果你有聚集,它的時間來設定GROUP BY語句。例如,如果算上在USERINFO每個的firstName所有的線路,你會寫「GROUP BY名字」:

SELECT firstName,count(*) FROM userInfo GROUP BY firstName 

這給你的條目在表中的每個不同的firstName數量。

第5步:有條件。這些是聚合物的條件。在前面的示例中,如果只需要表中具有多於5行的firstName的數據,則可以編寫SELECT firstName,count(*) FROM userInfo GROUP BY firstName HAVING count(*)>5

步驟6:使用ORDER BY進行排序。很容易...

這只是一個簡短的總結。還有很多更多的發現,但是在這裏編寫完整的SQL課程太長了......希望它有幫助!

+0

這並不能真正回答我的問題。我對我的例子的答案沒有那麼感興趣。我對你如何回答這個問題感興趣。如果你可以看看我的描述,然後向我展示構建查詢的思維過程,那就太棒了。 – Avery 2010-11-17 01:00:47

+0

問題是:我並不完全確定我的代碼是做你想做的,因爲你的問題有點不清楚。我們假設它是,並且您要查找的是從查詢結果開始,僅返回每個用戶對應於最新modifiedTimestamp的行。它是否正確?我會修改我的答案。 – 2010-11-17 01:03:20

0

你不能不瞭解表中的數據和所需的邏輯結果構造SQL。沒有背景給出這些表格可能的外觀和意義,並且您試圖收集的結果的描述對我來說沒有意義,所以我不打算猜測。

關於後一點......很少有人希望將時間戳值聯合使用多個來源。一般來說,當收集到類似的結果時,通常會進行某種審計/跟蹤。但是,如果您丟棄關於時間戳源的所有信息並且只計算最大值,那麼......具體到底是什麼?無論如何,數據和期望輸出的一個或多個示例,以及關於應用程序和whys的一些事情是必須使自己清楚。

在一定程度上我會讓你的最終聲明的形狀的任何預測,(假設你的任務仍然是讓每個用戶一個最大時間戳)那就是它會是這個樣子:

select u.firstname, u.lastname, user_max_time.userName, user_max_time.max_time 
from users u, 
(select (sometable).userName, max((sometable).(timestamp column)) 
from (data of interest) 
group by (sometable).userName) user_max_time 
where u.userName = user_max_time.userName 
order by max_time desc; 

您的任務在這裏將是用user_max_time子查詢內的()s替換爲有意義的東西並映射到您的需求。就複雜SQL的一般方法而言,主要建議是從最內層的子查詢中返回查詢(一路測試以確保性能良好,並且不需要中間表)。

無論如何,如果你有麻煩,並可以回來的例子,將很樂意提供幫助。

乾杯, 本

+0

您可以在SO上格式化代碼,方法是突出顯示它並單擊「101」按鈕。 – 2010-11-17 13:29:51

+0

是的,我意識到我發佈的表結構的意義是不透明的。爲了帶來更多的亮點:對於我們的應用程序,我們將始終擁有userProgram,但userInfo中的項目非常靈活。我們合併它們的原因是我們想要發生的最後一次更新,無論它是userProgram的集合還是userInfo中的一個元素。 – Avery 2010-11-18 14:42:06

1

至於F00說,這很簡單(R),如果您在設置方面認爲數據的。

問題的一個問題是,預期的輸出與規定的要求不符 - 描述中提到了isApproved列,但這不會出現在查詢或預期輸出中的任何位置。

這說明了這一點,寫入查詢的第一步是要清晰地知道您想要達到的什麼。這個問題的最大問題在於,這個問題沒有清楚地描述 - 相反,它從預期輸出的樣本表(如果我們有相應的預期輸入數據樣本會更有幫助)直接轉化爲你打算如何實現它。

據我瞭解,你想看到是用戶的列表(由用戶名,與其相關的名字和姓氏),連同上次任何相關的記錄是在任一用戶信息或userProgram修改表。

(目前尚不清楚是否要看到誰對上述任一其他表沒有關聯的活動的用戶 - 您提供的查詢意味着沒有,否則加入將外連接。)

所以,你希望用戶列表(由用戶名,與其相關的姓和名):

SELECT firstName, lastName, userName 
FROM user 
用的時間列表

在一起的記錄上次修改:

SELECT userName, MAX(modifiedTimestamp) 

...

在任一用戶信息或userProgram表:

...

FROM 
(SELECT userName, modifiedTimestamp FROM userInfo 
UNION ALL 
SELECT userName, modifiedTimestamp FROM userProgram 
) subquery -- <- this is an alias 

...

通過用戶名:

...

group by userName 

這兩組數據都需要通過自己的用戶名聯繫起來 - 所以最終的查詢變爲:

SELECT user.firstName, user.lastName, user.userName, 
     MAX(subquery.modifiedTimestamp) last_modifiedTimestamp 
FROM user 
JOIN 
(SELECT userName, modifiedTimestamp FROM userInfo 
UNION ALL 
SELECT userName, modifiedTimestamp FROM userProgram 
) subquery 
ON user.userName = subquery.userName 
GROUP BY user.userName 

在SQL的大多數版本,此查詢將返回一個錯誤user.firstName,並且不包括user.lastNameGROUP BY子句中也沒有總結。 MySQL允許使用此語法 - 在其他SQL中,因爲這些字段在功能上依賴於用戶名,所以在每個字段前添加MAX 將它們添加到分組中會實現相同的結果。

兩三附加分:

  • UNION和UNION ALL是不相同的 - 而後者沒有前者刪除重複;這使得前者更加處理器密集型。 因爲重複將被分組刪除,所以最好使用UNION ALL。
  • 許多人會寫入這個查詢,因爲用戶加入到用戶加入到userProgram的userInfo UNIONed ALL中 - 這是因爲許多SQL引擎可以更有效地優化這種類型的查詢。 在這一點上,這代表了不成熟的優化。
1

這裏有很多好東西。感謝所有貢獻的人。這是我發現有用的東西的快速總結,以及將建築物功能連接到建築查詢的一些其他想法。我希望我可以給每個人作爲優點徽章/積分,但我認爲只能有一個(答案),所以我會根據積分和個人幫助來選擇Traroth。

函數可以理解爲三個部分:輸入,過程,輸出。查詢可以被類似地理解。大多數查詢是這個樣子:

SELECT stuff FROM data WHERE data is like something 
  • SELECT部分是輸出。這裏有一些格式化輸出的功能(即使用AS

  • FROM部分是輸入。應該將輸入視爲一組數據;您將希望儘可能使其具體,使用適當的各種連接和子查詢。

  • WHERE部分與過程相似,但與FROM部分有很多重疊。 FROMWHERE部分都可以使用各種條件適當地減少數據池,以過濾掉不需要的數據(或僅包含所需的數據)。 WHERE部分也可以幫助格式化輸出。

以下是我砸開步驟:

  1. 開始思考你的輸出是什麼樣子。這東西進入SELECT部分。

  2. 接下來,您要定義您希望使用的數據集。 Traroth指出:「知道你需要的領域,寫下不同的對應表格之間的連接,這很容易......」這取決於你'易'的意思。如果你對編寫查詢很陌生,你可能會默認編寫內部連接(就像我做的那樣)。這並不總是最好的方式。 http://en.wikipedia.org/wiki/Join_(SQL)是理解不同種類的連接可能的很好的資源。

  3. 作爲上一步的一部分,考慮一下數據集中較小的部分,並構建您感興趣的完整數據集。在編寫函數時,您可以編寫子函數來幫助用更清晰的方式表達流程方式。與此類似,您可以編寫子查詢。 Mark Ba​​nnister在創建子查詢和使用別名方面的一個巨大提示。您將不得不重新配置您的輸出以使用此別名,但這非常關鍵。

  4. 最後,您可以使用各種方法來削減下來你的數據集,刪除數據你不感興趣的

一種方式來思考你所操作的數據是一個巨大的2- D矩陣:JOIN s使橫向更大,UNION縱向更大。所有其他濾鏡都設計得較小,以適合您的輸出。我不知道JOIN是否有「功能」類比,但UNION只是將兩個函數的輸出加在一起。

我意識到,雖然構建查詢不像編寫函數那樣有很多方法。例如,您可以在FROMWHERE區域中構建和削減數據集。對我來說關鍵是理解聯接並找出如何使用別名創建子查詢。