【问题标题】:array compiling from mysql help required需要从 mysql 帮助编译的数组
【发布时间】:2010-09-18 15:52:27
【问题描述】:

这一直困扰着我,我无法理解它。我将使用食品类比来尝试简化我的问题。

1000 名公众成员被要求从 13 种类型的脚垫中选择一种。这些选择然后根据他们的名字存储在一个 mysql 数据库中。

e.g.    billy   mary   etc.   etc.

milk....semi. .skimmed...

bread...white...brown....

cheese..edam.....edam....

fruit...apple...orange...

veg....potato...sprout...

meat....beef.....beef....

sweet..bonbons..liquorice..

fish...trout....salmon...

crisp....s&v....plain....

biscuit..hovis..rich tea..

wine.....red.....red.....

beer....stella..carlsburg..

carb....coke.....pepsi....

然后要求这 1000 人中的一个通过复选框选择从零到 13 的任意选项。

通过搜索数据库,有多少其他人选择了相同的品种?

在表格中显示他们的所有名字以及他们为所有 13 个品种选择的内容。

这有意义吗?我希望如此,因为它让我发疯。

【问题讨论】:

  • 也许您还可以展示您计划如何将这些信息存储在数据库中?桌子会是什么样子?
  • 你好,Hallgrim。当然。该表将食品和参与者名称作为文本字段。他们从每个字段中选择的内容都会记录在他们的名字上。它们的名称是唯一的,并且随着用户 ID 的增加而增加。只有一张桌子。
  • 那么表中有13个food stuff栏?
  • 嗨,达里尔。是的,一个名字和一个用户ID。
  • 我认为拥有 2 张桌子会容易得多,一张与用户有关,另一张与食物与 user_id 的关系。

标签: php mysql database arrays


【解决方案1】:

部分答案:

  • 通过排序和连接组成一组线性选择
  • 然后比较变成一个简单的 WHERE 子句

所以您首先要进行计算,将字符串放入某个字段,如字面意思“milk|semi|skimmed”。

【讨论】:

    【解决方案2】:

    假设你有一个简单的布局,那么你会有这样的东西(我将自己限制在三个类别中):

    PersonId  What_Milk  What_Bread  What_Cheese
           1  Semi       Wheat       Swiss
           2  Skimmed    Rolls       French
           3  Soy        Brown       Smelly
           4  Low Fat    Wheat       Swiss
    

    如果我理解正确,你的问题是这样的:

    当第 4 个人被要求选择 0 .. 3 个她的食物时,她可以选择“面包”和“奶酪”复选框,这意味着查询应该产生第 1 个人作为匹配项。对吧?

    SELECT
      PersonId,
      What_Milk,
      What_Bread,
      What_Cheese
    FROM
      FoodPreference
    WHERE
      PersonId != ?
      AND What_Milk   = IFNULL(NULLIF(?, ''), What_Milk)
      AND What_Bread  = IFNULL(NULLIF(?, ''), What_Bread)
      AND What_Cheese = IFNULL(NULLIF(?, ''), What_Cheese)
    

    您的复选框值稍后将转到问号占位符所在的位置。 (我已经用 IFNULL/NULLIF 替换了以前的 CASE WHEN 结构,它具有相同的效果,但对 PHP 准备语句更友好。

    如果未选中复选框(从而将值固定为某物),则将相应的列与其自身进行比较。这意味着它的值不会影响结果。如果其他列匹配,则将选择该行。

    这也意味着如果用户选择了个复选框,所有行将被返回。一个人选择的食物越多,匹配就越接近。

    在 PHP 中,我建议您使用 mysqli_prepare() 从查询字符串创建准备好的语句,并使用 mysqli_stmt_bind_param() 将实际值绑定到问号占位符。这比直接构建 SQL 字符串要安全得多。 PHP documentation has a whole lot of info on mysqli,看看吧。

    【讨论】:

    • 嗨托马拉克。是的,如果您的示例中的第 4 个人被要求选择并且她选择了复选框面包和奶酪,那么应该显示为这两种食品选择与她相同的每个人。因为我们需要支付他们选择的所有东西,所以选择应该是 * 而不是 PersonId?是的,越少.....
    • .....勾选的复选框越多结果。勾选复选框会缩小搜索范围。我不知道这个 CASE WHEN 是什么时候?现在就去查。
    • 是的,选择您需要的任何列,这只是一个示例。避免在生产代码中使用通用的“SELECT *”,它的效率较低。
    • 我赞同 Tomalak 的建议,即使用准备好的语句参数而不是将 PHP 变量插入 SQL 表达式。
    • 好的。那么,如果复选框值出现在您拥有的位置?空引号在做什么?
    【解决方案3】:
    SELECT  
        PersonId,  
        milk,  
        bread,  
        cheese
    FROM  FoodPreference
    WHERE  PersonId != :chosen_person_id  
        AND $milk= CASE WHEN :isset($_POST["milk"]))> '' THEN :isset($_POST["milk"]))   ELSE milk END  
        AND $bread= CASE WHEN :isset($_POST["bread"]))> '' THEN :isset($_POST["bread"]))  ELSE bread END  
        AND $cheese= CASE WHEN :isset($_POST["cheese"]))> '' THEN :isset($_POST["cheese"])) ELSE cheese END
    

    我走对了吗?

    【讨论】:

    • 尝试使用代码格式按钮来缩进你的代码,使其更具可读性。我猜你越来越近了……不知何故。既然我知道您使用 PHP,我将稍微更新一下我的示例。
    【解决方案4】:

    好的,我发布了一个回复,但 Tomalak 非常正确地指出了一个逻辑缺陷。我再试一次:

    将类别列为 13 列的替代方法是将它们列为表中的 13 行,如下所示:

    CREATE TABLE FoodPreference (
      PersonID   INT NOT NULL REFERENCES People,
      FoodCat    VARCHAR(10) NOT NULL REFERENCES FoodCategories,
      FoodChoice VARCHAR(10) NOT NULL,
      PRIMARY KEY (PersonID, FoodCat)
    );
    INSERT INTO FoodPreference VALUES
      (123, 'bread', 'white'),
      (123, 'milk', 'skim'),
      (123, 'cheese', 'edam'), ...
      (321, 'bread', 'brown'),
      (321, 'milk', 'whole'),
      (321, 'cheese', 'edam'), ...
    

    然后您可以使用如下查询,将所选人 (p1) 中的任何行与另一个人 (p2) 的具有相同食物选择的行匹配,然后再匹配该人的所有其他选择 (p3 ):

    SELECT DISTINCT p3.*
    FROM FoodPreference AS p1
      JOIN FoodPreference AS p2 
        ON (p1.FoodCat = p2.FoodCat AND p1.FoodChoice = p2.FoodChoice 
          AND p1.PersonID != p2.PersonID)
      JOIN FoodPreference AS p3 
        ON (p2.PersonID = p3.PersonID AND p2.FoodCat != p3.FoodCat)
    WHERE p1.PersonID = {(int)$chosen_person_id}
      AND p1.FoodCat IN ('milk', 'bread', 'cheese');
    

    WHERE 子句中的列表 'milk', 'bread', 'cheese' 是您需要根据应用程序中的 $_POST 变量在 PHP 代码中构建的内容。如果选中了一个复选框,请在列表中包含该食物类别。

    <?php
    $food_cat_array = array("'none'");
    $legal_food_cats = array('milk'=>1, 'bread'=>1, 'cheese'=>1, ...);
    foreach (array_intersect_key($_POST, $legal_food_cats) as $key => $checked) {
      if ($checked) {
        $food_cat_array[] = "'$key'"; 
      }
    }
    $in_predicate = join(',', $food_cat_array);
    

    【讨论】:

    • 嗨,比尔。非常感谢,看来您对这个答案已经考虑了很多。不幸的是,我的桌子已经修好了。我可以将数据复制到另一个中,但如果可能的话,我真的想避免这种情况。我们可以研究托马拉克的答案吗?我带着它去那里。我已经放弃了食物.....
    • 现在正在使用我的实际值。不确定我是否正确比较。请参阅下面的我的新代码。得到一些mysql错误。无效的结果资源等。
    【解决方案5】:
    $sql = mysql_query("        
        SELECT  
        *
        FROM  ".$prefix."_users
        WHERE  username !='$username'  
        AND req_country   = IFNULL(NULLIF($bcountry, ''), req_country)  
        AND req_region  = IFNULL(NULLIF($bregion, ''), req_region)  
        AND req_type = IFNULL(NULLIF($btype, ''), req_type)
        AND req_beds = IFNULL(NULLIF($bbeds, ''), req_beds)
        AND req_value = IFNULL(NULLIF($bvalue, ''), req_value)
        AND country   = IFNULL(NULLIF($scountry, ''), country)  
        AND region  = IFNULL(NULLIF($sregion, ''), region)  
        AND type = IFNULL(NULLIF($stype, ''), type)
        AND beds = IFNULL(NULLIF($sbeds, ''), beds)
        AND value = IFNULL(NULLIF($svalue, ''), value)
        AND pool = IFNULL(NULLIF($spool, ''), 'Yes')
        AND garage = IFNULL(NULLIF($sgarage, ''), >0) 
                AND disabled = IFNULL(NULLIF($sdisabled, ''), 'Yes')");
    $num = mysql_num_rows($sql);
    echo "Total matches ($num): <br><br>";
            while($row = mysql_fetch_array($sql)){...etc
    

    【讨论】:

    • 请记住,AND 的优先级高于 OR。您可能应该在九个车库术语周围添加括号。
    • 应该是“req_country = IFNULL(NULLIF('$bcountry', ''), req_country)”,以此类推。正如比尔所说,所有的“或”条件都需要放在一组括号中。
    • 谢谢。我已经编辑了代码,现在看起来如何? Pool 和 Disabled 在数据库中保存为 Yes 和 No。如果复选框已被勾选并且值为 Yes,我只希望选择这些行;所以我做对了吗?或者这些应该是不?我也可以只说车库的 >0 吗?
    • 不,你不能在那里说 >0,它不是有效的语法。尝试将您的车库术语替换为: CASE IFNULL($sgarage, '') WHEN '' THEN Garage IN (0,1,2,3,4,5,6,7,8,9) ELSE Garage = $sgarage END
    • @Nigel:我认为你需要花更多时间学习 SQL(和 PHP)。无意冒犯,但你看得很……嗯……没有受过教育。我们已经向你展示了该怎么做,我猜你只是需要更多的时间来连接这些位。
    【解决方案6】:
    case "display_results":
    
    if ($bcountry = !isset($_POST["bcountry"])){
          $bcountry = "No";
          }else {
          $bcountry = "Yes";
          }
    if ($bregion = !isset($_POST["bregion"])){
          $bregion = "No";
          }else {
          $bregion = "Yes";
          }
    if ($btype = !isset($_POST["btype"])){
          $btype = "No";
          }else {
          $btype = "Yes";
          }
    if ($bbeds = !isset($_POST["bbeds"])){
          $bbeds = "No";
          }else {
          $bbeds = "Yes";
          }
    if ($bvalue = !isset($_POST["bvalue"])){
          $bvalue = "No";
          }else {
          $bvalue = "Yes";
          }
    if ($scountry = !isset($_POST["scountry"])){
          $scountry = "No";
          }else {
          $scountry = "Yes";
          }
    if ($sregion = !isset($_POST["sregion"])){
          $sregion = "No";
          }else {
          $sregion = "Yes";
          }
    if ($stype = !isset($_POST["stype"])){
          $stype = "No";
          }else {
          $stype = "Yes";
          }
    if ($sbeds = !isset($_POST["sbeds"])){
          $sbeds = "No";
          }else {
          $sbeds = "Yes";
          }
    if ($svalue = !isset($_POST["svalue"])){
          $svalue = "No";
          }else {
          $svalue = "Yes";
          }
    if ($spool = !isset($_POST["spool"])){
          $spool = "No";
          }else {
          $spool = "Yes";
          }
    if ($sgarage = !isset($_POST["sgarage"])){
          $sgarage = "No";
          }else {
          $sgarage = "Yes";
          }
    if ($sdisabled = !isset($_POST["sdisabled"])){
          $sdisabled = "No";
          }else {
          $sdisabled = "Yes";
          }
    
         $result = mysql_query("SELECT * FROM  ".$prefix."_users WHERE  username!='$username' 
    AND (('$bcountry'='Yes' AND req_country= '$country') OR ('$bcountry'='No')) 
    AND (('$bregion'='Yes' AND req_region= '$region') OR ('$bregion'='No'))
    AND (('$btype'='Yes' AND req_type= '$type') OR ('$btype'='No'))
    AND (('$bbeds'='Yes' AND req_beds= '$beds') OR ('$bbeds'='No'))
    AND (('$bvalue'='Yes' AND req_value= '$value') OR ('$bvalue'='No'))
    AND (('$scountry'='Yes' AND country= '$req_country') OR ('$scountry'='No'))
    AND (('$sregion'='Yes' AND region= '$req_region') OR ('$sregion'='No'))
    AND (('$stype'='Yes' AND type= '$req_type') OR ('$stype'='No'))
    AND (('$sbeds'='Yes' AND beds= '$req_beds') OR ('$sbeds'='No'))
    AND (('$svalue'='Yes' AND value= '$req_value') OR ('$svalue'='No'))
    AND (('$spool'='Yes' AND pool= 'Yes') OR ('$spool'='No'))
    AND (('$sgarage'='Yes' AND garage>=1 ) OR ('$sgarage'='No'))
    AND (('$sdisabled'='Yes' AND disabled= 'Yes') OR ('$sdisabled'='No'))
    ")or die("MySQL ERROR: ".mysql_error());
    $number = mysql_num_rows($result);
    

    【讨论】:

    • 我希望复选框在搜索结果显示后保持勾选状态(这样人们就可以看到他们选择的内容)。我尝试将 $ticked1、$ticked2 等添加到复选框选项中,然后添加 if ($bcountry=='Yes'){$ticked1='Checked'} 等,但它似乎不起作用。有什么帮助吗?