4

我目前正在尝试用 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       |

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

例如:

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

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   |

请注意,马克是如何不包括在内的,因为他不满足自上次施肥以来 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 中。

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

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

非常感谢所有阅读了整个问题(很长)的人,我希望你们中的一些人理解我的需求并可以帮助我!

4

2 回答 2

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 作为表名,在最后添加错误喷射或邮政编码的附加参数。(即不是 T_ojobs 或 T_dif)

于 2013-05-21T11:12:05.350 回答
0

我找到了我的问题的答案,感谢 Waygood 的帮助,他帮助我理解ONLEFT 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子句中使用多个参数的相同想法。LEFT JOIN

再次感谢 Waygood 的帮助。

于 2013-05-22T05:50:59.273 回答