对遍布多个表的数据进行复杂的MySQL查询(PHP)

时间:2013-05-21 10:17:39

标签: php mysql sql

我目前正在尝试解决MySQL和PHP的复杂问题。

以下是我所拥有的表格的示例:

客户名单:

table_clients
Client_ID | Client_Name | Address         | Zip Code |
----------|-------------|-----------------|----------|
1         | Mark        | 127 Park Ave    | 12235    |
2         | John        | 6 Freeman Drive | 12899    |
3         | Allan       | 450 Clever Rd   | 12235    |

服务清单:

table_services
Service_ID | Service_Name | Service_Price |
-----------|--------------|---------------|
1          | Fertilizer   | 100.00        |
2          | Bug Spray    | 50.00         |
3          | Seeds        | 20.00         |

下一个表存储哪个客户有哪些服务(一个或多个),服务状态及完成日期(如果适用):

table_jobs
Job_ID | Client_ID | Service_ID | Status | Date_Done  |
-------|-----------|------------|--------|------------|
1      | 1         | 1          | done   | 2013-05-01 |
2      | 1         | 3          | active | NULL       |
3      | 2         | 1          | active | NULL       |
4      | 2         | 2          | active | NULL       |
5      | 3         | 1          | active | NULL       |
6      | 3         | 3          | active | NULL       |

现在是棘手的部分。有些服务需要与其他服务有一定的时差。例如,如果一个客户在过去30天内收到肥料,则无法收到种子。为了跟踪这一点,我有第三个表格,其中包含以下信息:

table_time_difference
Service_ID_1 | Service_ID_2 | Time_Diff |
-------------|--------------|-----------|
1            | 3            | 30d       |
1            | 4            | 7d        |
2            | 4            | 14d       |
4            | 5            | 14d       |

现在所有内容都存储在数据库中(请记住,可能有数十种服务和数千个客户端),我正在尝试获取具有某些服务或不具有某些服务的客户端行,同时始终尊重时差。< / p>

例如:

我希望所有应该收到Fertilizer的客户都应该返回:

Client_ID | Client_Name | Zip Code | Job_ID | Service_ID | Service_Name |
----------|-------------|----------|--------|------------|--------------|
2         | John        | 12235    | 3      | 1          | Fertilizer   |
3         | Allan       | 12145    | 5      | 1          | Fertilizer   |

现在,如果我想做所有接受肥料和虫子喷雾的客户:

Client_ID | Client_Name | Zip Code | Job_ID | Service_ID | Service_Name |
----------|-------------|----------|--------|------------|--------------|
2         | John        | 12235    | 3      | 1          | Fertilizer   |
2         | John        | 12235    | 4      | 2          | Bug Spray    |

如果我想要在邮政编码12235中接收种子的所有客户:

Client_ID | Client_Name | Zip Code | Job_ID | Service_ID | Service_Name |
----------|-------------|----------|--------|------------|--------------|
3         | Allan       | 12235    | 6      | 3          | Fertilizer   |

请注意Mark不包括在内,因为他不符合自上次肥料服务以来的30天要求。

我已经尝试了各种各样的JOINS的许多不同的选项,但从来没有找到一个像所描述的那样工作的解决方案。我最接近的是通过PHP生成子查询,并在一个大查询中加入它们。

例如,我的一次尝试看起来像这样(对于上面的最后一个预期结果):

SELECT c.Client_ID,
       c.Client_Name,
       c.Zip_Code,
       j.Job_ID,
       s.Service_ID,
       s.Service_Name
FROM clients c
LEFT JOIN jobs j
    ON j.Client_ID = c.Client_ID
LEFT JOIN services s
    ON s.Service_ID = j.Service_ID
WHERE s.Service_ID = "1"
   && c.Zip_Code = "12235"
   && c.Client_ID NOT IN (
                          SELECT Client_ID
                          FROM jobs
                          WHERE Status = "done"
                             && Date_Done < (UNIX_TIMESTAMP() - 2592000)
                         )
  • 请注意,子查询是由PHP脚本生成的,该脚本执行查找与所请求服务相对应的限制以及该服务的最小时间差,因为对同一服务可能存在多个限制,我不知道是否我可以用纯SQL做到这一点。

现在,上面显示的查询确实适用于那个确切的场景(虽然它非常慢),它会中断并且我无法使其适应我的其他需求(包含或排除的多个服务)。

告诉我您是否需要任何其他信息,或者您是否愿意进一步讨论。

非常感谢所有读完整个问题的人(很长时间),我希望你们中的一些人能够理解我的需求并帮助我!

2 个答案:

答案 0 :(得分:1)

以下内容可能有所帮助:

这将拉出未完成的订单(没有限制)

SELECT *
FROM table_jobs AS T_job, table_services AS T_ser, table_clients AS T_cli
WHERE T_job.Client_ID=T_cli.Client_ID
AND T_job.Service_ID=T_ser.Service_ID
AND T_job.Status='active'

这应该提取先前已完成订单限制的订单 但是Time_Diff应该是几天(即删除d

SELECT *
FROM (table_jobs AS T_job, table_services AS T_ser, table_clients AS T_cli)
LEFT JOIN (table_time_difference AS T_dif, table_jobs AS T_ojobs)
ON (
    AND T_job.Service_ID=T_dif.Service_ID_1
    AND T_dif.Service_ID_2=T_ojob.Service_ID
    AND T_ojobs.Date_Done > DATE_SUB(CURDATE(), INTERVAL T_dif.Time_Diff DAY)
    AND T_ojobs.Status='done'
)
WHERE T_job.Client_ID=T_cli.Client_ID
AND T_job.Service_ID=T_ser.Service_ID
AND T_job.Status='active'
AND T_ojobs.Job_ID IS NULL

然后,您可以使用T_job,T_ser或T_cli作为表名,在最后添加bug喷涂或邮政编码的附加参数。 (即不是T_ojobs或T_dif)

答案 1 :(得分:0)

我找到了问题的答案,感谢Waygood帮助我理解ON LEFT JOIN条款的不同用法。

这是满足我列举的所有需求的查询:

# Select desired columns

SELECT j.`Job_ID`,
       j.`Client_ID`,
       c.`Name`,
       j.`Service_ID`,
       s.`Service_Name`,
FROM (
      jobs j,
      clients c,
      Services s
      )

# Use LEFT JOIN to filter out services that meet the constraints

LEFT JOIN (
           time_difference diff,
           jobs j2
          )
ON (
    j.`Service_ID` = diff.`Service_ID_1`
    && j.`Client_ID` = j2.`Client_ID`
    && diff.`Service_ID_2` = j2.`Service_ID`
    && j2.`Date_Done` > (UNIX_TIMESTAMP() - diff.`Time_Diff`)
    && j2.`Status` = 'done'
)

# Use LEFT JOIN to filter out jobs that have specific restrictions

LEFT JOIN jobs_constraints jconst
ON (
    j.`id` = jconst.`Job_ID`
    && jconst.`value` > UNIX_TIMESTAMP()
)

#Add one LEFT JOIN for every service that a client must or must not have

LEFT JOIN jobs j3
ON (
    j.`Client_ID` = j3.`Client_ID`
    && j3.`Status` = "active"
    && j3.`Service_ID` = "1"
)
LEFT JOIN jobs j4
ON (
    j.`Client_ID` = j4.`Client_ID`
    && j4.`Status` = "active"
    && j4.`Service_ID` = "2"
)
LEFT JOIN jobs j5
ON (
    j.`Client_ID` = j5.`Client_ID`
    && j5.`Status` = "active"
    && j5.`Service_ID` = "3"
)

# END automatically generated blocks

WHERE j.`Status` = "active"
   && j2.`Job_ID` IS NULL
   && j.`Client_ID` = c.`Client_ID`
   && j.`Service_ID` = s.`Service_ID`
   && jconst.`id` IS NULL

   # Add one AND clause for every service that a client must
   # or must not have use IS NULL if client must not have service
   # or IS NOT NULL if client must have

   && j3.`Job_ID` IS NULL
   && j4.`Job_ID` IS NOT NULL
   && j5.`Job_ID` IS NOT NULL

此查询仍然需要生成一些PHP,但它非常简单,并且只有2个区域需要动态调整。

自从我的问题以来我添加了一些其他参数以允许更多控制,但它仍然围绕在我正在做的ON的{​​{1}}子句中使用多个参数的相同想法。

再次感谢Waygood的帮助。