2012-08-24 63 views
1

MySQL查詢順序我有一個表女巫有45列,但尚未完成只有幾個。這個表是不斷更新和添加等。在我的自動完成功能,我想選擇這些記錄排序最完整的領域(我希望你明白)?以「最全領域」

解決的辦法之一是創建另一個(下稱「等級」字段),並創建一個PHP函數,選擇*記錄,並給出每個記錄的等級。

...但我想知道是否有這樣做只是白衣一個ORDER BY的一個更簡單的方法?

+0

你能向我們提供您的表結構的一個樣本,你使用/不使用的數據類型。 –

+0

該表格很簡單:啓動白名稱,並且它們都是VARCHAR白名單最多200個字符 – faq

+1

是否要按行中填充字段數(每個記錄)或填充字段數一列(每桌)? –

回答

5

MySQL有沒有功能,指望一排非NULL字段數,據我所知。

所以,我能想到的唯一的辦法就是使用一個明確的條件:

SELECT * FROM mytable 
    ORDER BY (IF(column1 IS NULL, 0, 1) 
      +IF(column2 IS NULL, 0, 1) 
      ... 
      +IF(column45 IS NULL, 0, 1)) DESC; 

...它是醜陋的罪過,但應該做的伎倆。

你也可以設計一個觸發器來增加一個額外的列「fields_filled」。觸發器花費在UPDATE上,45個IF對你造成傷害SELECT;你必須建立更方便的模型。

請注意,索引所有字段以加快SELECT會在更新時花費你(並且45個不同的索引的成本可能高於select上的表掃描,而不是索引字段是VARCHAR)。運行一些測試,但我相信45-IF解決方案可能是最好的整體。

UPDATE如果可以返工你的表結構在一定程度上規範它,你可以把領域的my_values表。然後你會有一個「頭表」(也許只有一個唯一的ID)和一個「數據表」。空字段根本不存在,然後您可以通過使用RIGHT JOIN來排序填充字段的數量,用COUNT()對填充字段進行計數。這也會大大加快UPDATE操作,並且可以讓您有效地使用索引。

實例(從表設置兩個規範化表設置)

讓我們說我們有一組Customer記錄。我們將有一小段「強制性」數據,如ID,用戶名,密碼,電子郵件等。那麼我們可能會有更多的「可選」數據子集,如暱稱,頭像,出生日期等。作爲第一步,讓我們假設所有這些數據都是varchar(與其中每列可能有自己的數據類型的單表解決方案相比,這初看起來似乎是一個限制)。

所以我們有一個表像,

ID username .... 
1 jdoe  etc. 
2 jqaverage etc. 
3 jkilroy  etc. 

然後我們有可選的數據表。這裏John Doe填補了所有領域,Joe Q.平均只有兩個,而Kilroy沒有(即使他在這裏)。

userid var val 
1  name John 
1  born Stratford-upon-Avon 
1  when 11-07-1974 
2  name Joe Quentin 
2  when 09-04-1962 

爲了再現在MySQL中「單個表」的輸出,我們必須創建一個相當複雜的VIEW有很多LEFT JOIN秒。這種觀點將仍然是非常快的,如果我們有一個基於(userid, var)(甚至更好,如果我們用一個數字常量或SET而不是一個varchar的爲var數據類型的索引:

CREATE OR REPLACE VIEW usertable AS SELECT users.*, 
    names.val AS name // (1) 
FROM users 
    LEFT JOIN userdata AS names ON (users.id = names.id AND names.var = 'name') // (2) 
; 

每個字段在我們的邏輯模型,例如,「名稱」將包含在可選數據表中的元組(id,'name',value)中

並且它將產生表格中的一行,上面的查詢,參考部分的線的形式LEFT JOIN userdata AS <FIELDNAME>s ON (users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>')的(2)。因此,我們可以動態地通過用動態部1,TEX級聯上述查詢的第一個TextLine構造查詢T'從用戶的和動態建成第二節

一旦我們做到這一點,選擇在視圖上是完全相同之前 - 但現在他們取兩個規範化表通過JOIN的數據。

EXPLAIN SELECT * FROM usertable; 

會告訴我們,添加列到這個設置並不明顯減緩行動,即,這個解決方案適用還算不錯。我們要麼更新強制性數據表,要麼更新可選數據表的單個行。我們需要修改INSERT(我們只插入強制性數據,並且只在第一個表中)和UPDATE:但是如果目標行不在那裏,那麼它必須被插入。

所以我們必須用 'UPSERT' 來代替

UPDATE usertable SET name = 'John Doe', born = 'New York' WHERE id = 1; 

,在這種情況下

INSERT INTO userdata VALUES 
     (1, 'name', 'John Doe'), 
     (1, 'born', 'New York') 
    ON DUPLICATE KEY UPDATE val = VALUES(val); 

(我們需要一個UNIQUE INDEX on userdata(id, var)ON DUPLICATE KEY工作)。

取決於行大小和磁盤問題,這種變化可能會產生可觀的性能增益。

請注意,如果未執行此修改,現有查詢將不會產生錯誤 - 它們將悄然失敗

這裏有個例子,我們修改兩個用戶的名稱;一個在記錄上有一個名字,另一個有NULL。第一個是修改的,第二個不是。

mysql> SELECT * FROM usertable; 
+------+-----------+-------------+------+------+ 
| id | username | name  | born | age | 
+------+-----------+-------------+------+------+ 
| 1 | jdoe  | John Doe | NULL | NULL | 
| 2 | jqaverage | NULL  | NULL | NULL | 
| 3 | jtkilroy | NULL  | NULL | NULL | 
+------+-----------+-------------+------+------+ 
3 rows in set (0.00 sec) 
mysql> UPDATE usertable SET name = 'John Doe II' WHERE username = 'jdoe'; 
Query OK, 1 row affected (0.00 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 
mysql> UPDATE usertable SET name = 'James T. Kilroy' WHERE username = 'jtkilroy'; 
Query OK, 0 rows affected (0.00 sec) 
Rows matched: 0 Changed: 0 Warnings: 0 
mysql> select * from usertable; 
+------+-----------+-------------+------+------+ 
| id | username | name  | born | age | 
+------+-----------+-------------+------+------+ 
| 1 | jdoe  | John Doe II | NULL | NULL | 
| 2 | jqaverage | NULL  | NULL | NULL | 
| 3 | jtkilroy | NULL  | NULL | NULL | 
+------+-----------+-------------+------+------+ 
3 rows in set (0.00 sec) 

要知道每一行的排名,對於那些確實有秩的用戶,我們只檢索用戶數據行的每個ID計數:

SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id 

我們提取「充滿狀態行「命令,我們這樣做:

SELECT usertable.* FROM usertable 
    LEFT JOIN (SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id) AS ranking 
ON (usertable.id = ranking.id) 
ORDER BY rank DESC, id; 

LEFT JOIN確保rankless個人得到檢索過,並通過id額外的排序確保人們以identica l排名總是以相同的順序出來。

+0

是的,謝謝。這是最好的答案! – faq