2012-08-24 52 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