MySQL表连接,嵌套select语句,还是创建视图?

时间:2011-02-17 21:21:30

标签: php mysql select

免责声明:我先在其他网站上发布此内容

我有一个大约200列宽的表(res_table)。其中一列名为“feature_lk”,它由一串数字“|”组成分隔。这些数字代表功能类别,它们位于另一个名为“features”的表中

感谢这个帖子:http://www.kirupa.com/forum/showthread.php?t=224203我想出了如何解析这些功能!

现在我的问题是如何查找它们?我觉得我需要加入我的两个表,但我不确定如何,或者我需要为我解析的每个功能执行另一个选择查询..这就是我所要做的(删除连接字符串)用于发布目的)

PHP Code:
<?php 
$sql = ("SELECT * FROM res_table"); 
$result = mysql_query($sql); 

while($row = mysql_fetch_array($result)) 
{ 
    $feature_string = $row['features_lk']; 
    $features = explode( '|', $feature_string ); 

    foreach( $features as $feature ) { 
        $feature = trim( $feature ); 
        echo $feature.': '; 

        $sql2 = "SELECT * from features where features.feature_id like $feature"; 
        $result2 = mysql_query($sql2); 
        while ($row2 = mysql_fetch_array($result2)) 
        { 
            $feat_desc = $row2['feature_description']; //this is another column in the features table 
            echo $feat_desc . '<br>'; 
        } 
    } 
    echo '<br>'; 
} 
?>

这样可行,因为当我运行它时,我会得到看起来像这样的结果:

13: None
62: Water Softener - Rented
71: Full
168: Barn
222: Storage Shed
226: Walkout
309: Detached
347: 2 Story
384: Attic Storage
439: Laundry Hook Up
466: Rural
476: Trees
512: School Bus
562: Mud Room
563: Pantry
2273: Septic Tank
643: Private Well

我的问题是:有更好的方法吗?主res_table中大约有10k行,只有几百个命中,你可以看到执行的select语句的数量在任何时候都会大大增加。

我确定这是PHP + MySQL 101的东西,但我只是一个初学者,所以任何想法?提前谢谢。

4 个答案:

答案 0 :(得分:2)

当您在列中存储多条信息时,表格不会被标准化。在feature_lk上进行查找一定会很慢而且很困难。 feature_lk应该成为自己的表格:

表feature_lk:

  • res_table_id FK to res_table
  • feature_id FK到功能表
  • 主键(res_table_id,feature_id)

然后你的查询是:

SELECT f.* from features f 
  JOIN feature_lk lk ON (f.id=lk.feature_id) 
  JOIN res_table r ON (lk.res_table_id=r.id);

仅限一个查询。没有循环。没有解析出这些功能。

ETA

用任意字符

分割任意长度字符串的存储过程
DELIMITER $$

DROP PROCEDURE IF EXISTS `dorepeat` $$
CREATE PROCEDURE `dorepeat`(in ToBeSplit LONGTEXT , in Splitter CHAR)
Begin

DECLARE TotalLength INT;
DECLARE SplitterPosition INT;
DECLARE SubstringLength INT;
DECLARE SubstringStart INT;

DROP Table if exists Split_Values;
CREATE temporary TABLE Split_Values (split varchar(255));

SET TotalLength = LENGTH(ToBeSplit);
SET SplitterPosition = LOCATE(Splitter, ToBeSplit);
SET SubstringStart = 1;

ss: WHILE SplitterPosition < TotalLength DO
        IF SplitterPosition!=0 THEN 
                SET SubstringLength = SplitterPosition - SubstringStart;
                Insert into Split_Values VALUES (SUBSTRING(ToBeSplit,SubstringStart,SubstringLength));
                SET SubstringStart = SplitterPosition+1;
                SET SplitterPosition = LOCATE(Splitter, ToBeSplit, SplitterPosition+1);
        ELSE
                Insert into Split_Values VALUES (SUBSTRING(ToBeSplit,SubstringStart));
                SET SplitterPosition=TotalLength;
        END IF;
END WHILE ss;
End $$

DELIMITER ;

在另一个过程中使用dorepeat创建带有res_table_id和每个功能的临时表:

DELIMITER $$

DROP PROCEDURE IF EXISTS `multido` $$
CREATE PROCEDURE `multido`()
Begin
DECLARE done INT default 0;
DECLARE rt_id INT (10);
DECLARE features LONGTEXT;
DECLARE mycur cursor for select distinct res_table_id, feature_lk from res_table WHERE feature_lk!='';
DECLARE continue handler for sqlstate '02000' set done=1;
drop table if exists tmpfeatures;
create temporary table tmpfeatures( res_table_id int(10),  feature varchar(255));
open mycur;
repeat
  fetch mycur into rt_id,features;
  call dorepeat(features,'|');
  insert into tmpfeatures select rt_id, trim(split) from Split_Values;
until done end repeat;
close mycur;

End $$

DELIMITER ;

答案 1 :(得分:0)

你感觉到这里糟糕的数据库建模的痛苦。如果您对数据库模式有任何控制权,那么您应该对其进行修复,以便对其进行适当的规范化。无论何时,当您在数据库中看到管道(或逗号,制表符或其他任何)列表时,您都应该非常怀疑它。

您的表和类别之间应该有一个连接表,通常命名为RES_CATEGORIES,其中包含RES的ID和CATEGORIES的ID。这是在关系数据库中建模多对多关系的标准方法。

如果您无法控制架构,那么您最好的办法就是在代码中解析它并执行单独的查询(或查询)以获取类别信息。您至少可以在where子句中指定多个类别ID,以减轻其痛苦。

答案 2 :(得分:0)

根据我在你的问题中的理解,你需要一个中间表。例如,您有表tbl_user和tbl_features,其中用户可以订阅许多功能,并且每个功能都可以由许多用户订阅。

使用额外的表tbl_userfeatures {userFeatureID,userID,featureID}可以更好地管理您的数据库,该表链接其他两个表并允许您添加不同的组合。

答案 3 :(得分:0)

一个简单的优化步骤是在一步中获取功能,而不是在它们上面循环。像这样:

$result = mysql_query('SELECT * FROM res_table');
while ($row = mysql_fetch_array($result)) {
    $features = str_replace('|', ',', $features);
    $result2 = mysql_query("SELECT * FROM features WHERE feature_id IN $features");
    while ($row2 = mysql_fetch_array($result2) {
        printf('%d: %s', $row2['feature_id'], $row2['feature_description']);
    }
}

这是res_table中每一行的一个查询,而不是每个功能的一行。

但在此之前,请先听取其他回复。如果您能够将数据库模式更改为更合理的模式,请执行此操作!