【问题标题】:PHP/MySQL - Next/Previous buttons based on date/time with dupliate date/timesPHP/MySQL - 基于日期/时间的下一个/上一个按钮,具有重复的日期/时间
【发布时间】:2021-01-15 15:26:08
【问题描述】:

假设我有一个带有项目的日历,当您单击其中一个时,我想提供一个下一个/上一个按钮。例如:

ID      Time    Name
123     8:30    Apple
432     9:00    1 Plumb
43      9:00    2:2 Kale
663     9:00    Apples
12      9:00    Berry
235     9:00    Carrot
62      11:00   Tomato
23      11:30   Pepper

在日历上列出这些很容易:

SELECT *
FROM fruits
WHERE date="2020-09-30"
ORDER BY 
    STR_TO_DATE(`time`, '%H:%i') ASC,
    name ASC;

我遇到的问题是获得准确的下一个/上一个项目,并且只有当所有项目同时开始时才会出现问题,例如上午 9:00:

# Previous Button - Should get Apples, but it returns `Carrots`
SELECT 
    name
FROM fruits 
WHERE 
    id != '12' #Berry
AND TIMESTAMP(date, time) <= TIMESTAMP('2020-09-30', '9:00') 
ORDER BY 
    date DESC, 
    STR_TO_DATE(time, '%H:%i') DESC, 
    name DESC
LIMIT 1;

# Next Button - Should get Carrot, but it returns `1 Plumb`
SELECT 
    name
FROM fruits 
WHERE 
    id != '12' #Berry
AND TIMESTAMP(date, time) >= TIMESTAMP('2020-09-30', '9:00') 
ORDER BY 
    date ASC, 
    STR_TO_DATE(time, '%H:%i') ASC, 
    name ASC
LIMIT 1;

我明白为什么,但我不知道如何解决。

如果我可以过滤字母数字,比如 REGEX '^[Apa-p1-0]',但我必须做大量的 PHP 来确定它是数字还是 alpha,然后处理很快就会得到的变量令人困惑。另外,我可能需要循环更混乱的数组。

我可以使用一个变量,动态地对行进行编号,但是我还需要使用 PHP 来确定我在循环中的位置,然后找到下一个,我需要获取几十行来确保我不会错过任何一个。

要明确一点——这张表有数百万行,数据经常变化,多人可以随时更改数据,所以我需要一些快速和动态的东西。

我创建了一个包含上述所有数据和查询的 DB Fiddle,以便更轻松地提供任何帮助:

https://www.db-fiddle.com/f/a5qnbhUgcG9j9irkzawKHx/0

有什么想法吗?

更新 :: Nicks 回答的解决方案

查看他的答案以获得完整的解释。他的回答更加动态并且不需要 PHP,我想绕过子查询,所以我最终使用了一点 PHP。非常感谢,如果没有你的帮助,我永远不会在一个查询中解决这个问题!

PHP:

$timestamp = strtotime($date.' '.$time);

MySQL

# Previous
SELECT 
    name
FROM fruits 
WHERE 
    (
        UNIX_TIMESTAMP(TIMESTAMP(`date`, `time`)) - $timestamp < 0
           OR 
        UNIX_TIMESTAMP(TIMESTAMP(`date`, `time`)) - $timestamp = 0 
        AND name < '$name'
    )
ORDER BY 
    UNIX_TIMESTAMP(TIMESTAMP(`date`, `time`)) - $timestamp DESC, 
    name DESC
LIMIT 1;

# Next
SELECT 
    name
FROM fruits 
WHERE 
    (
        UNIX_TIMESTAMP(TIMESTAMP(`date`, `time`)) - $timestamp > 0
           OR 
        UNIX_TIMESTAMP(TIMESTAMP(`date`, `time`)) - $timestamp = 0 
        AND name > '$name'
    )
ORDER BY 
    UNIX_TIMESTAMP(TIMESTAMP(`date`, `time`)) - $timestamp ASC, 
    name ASC
LIMIT 1;

【问题讨论】:

  • 由于您将决定订单是什么,如果您事先将其读入可迭代的东西(如堆栈或列表)中会怎样?
  • 我想到了这个,问题是这张表有几百万行,经常变化,同样的数据可以显示在多个人同时使用的略有不同的集合中,所以就我知道没有可行的方法来预先确定订单,它必须是动态的。但是,如果更改原始顺序可能会解决问题,我愿意将其作为解决方案。无论哪种方式,使用我的小提琴的例子都会受到赞赏。
  • 那你刷新表了吗?当你这样做时,我会创建数组。如果不是那么点击,但那是很多来回的服务器 idk。
  • 每次显示项目日历时,是的,它只是获取数据。我有一些非常快速的索引设置。我不认为我可以创建一个数组,因为从技术上讲,您可以单击“September”中的第一项,然后您需要“August”中最后一项的上一个按钮,因此该数组必须是数百个项目很长,即使那样,您也可能会发现自己处于超出您考虑范围的情况。
  • 为什么Carrot 应该在Berry 之后?同时...

标签: php mysql sorting


【解决方案1】:

我认为您可以通过计算每个条目的时间戳与 id = 12 (Berry) 的时间戳之间的差异,并选择差异为 &lt;= 0name &lt; 'Berry' 的那些行来获得所需的结果上一行或下一行的差异为&gt;= 0name &gt; 'Berry'。例如,对于上一行:

SELECT name AS previous,
       UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) -
    (SELECT UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) 
     FROM fruits 
     WHERE id = 432) AS tsdiff
FROM fruits
HAVING tsdiff < 0 OR tsdiff = 0 AND name < (SELECT name FROM fruits WHERE id = 432)
ORDER BY tsdiff DESC, name DESC
LIMIT 1

输出:

previous    tsdiff
Apple 4     -84600

注意我使用了HAVING 子句来允许我使用别名。这可能不如使用WHERE 子句执行得好,但是这将需要重复时间戳计算:

SELECT 
    name AS previous
FROM fruits 
WHERE UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) -
    (SELECT UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) 
     FROM fruits 
     WHERE id = 432) < 0
     OR UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) -
    (SELECT UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) 
     FROM fruits 
     WHERE id = 432) = 0 AND name < (SELECT name FROM fruits WHERE id = 432)
ORDER BY 
    UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) -
    (SELECT UNIX_TIMESTAMP(STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')) 
     FROM fruits 
     WHERE id = 432) DESC,
    name DESC
LIMIT 1

db-fiddle 上两个查询的演示。

还请注意,您应该将time 值存储在TIME 列中,或者至少将格式设置为%H:%i 的列中,以确保TIMESTAMP 按预期工作。要解决我使用STR_TO_DATE 的问题,如果数据存储正确,您可以使用

TIMESTAMP(date, time)

我用过的地方

STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %k:%i')

【讨论】:

  • 哇,这是一个非常有创意的解决方案,谢谢。不幸的是,它似乎只有在一切都在同一时间时才有效,比如 9:00。一旦您开始更改日期和时间,它就会开始失败。例如选择 ID #432:db-fiddle.com/f/5LaFmC15SuizALfEMrucXn/0
  • @Maurice 道歉,我的情况是错误的。我已更正查询并更新了演示链接。请注意,在评论中的演示中,您在下一个查询中的排序错误,它们都应该是 ASC,而不是 DESC。看我的演示。
  • 我可以吻你。你甚至不会相信我花了多少时间尝试不同的解决方案。我设法用一点点 PHP 找到了一个更清洁的解决方案,我会尽快为将来遇到此问题的任何人发布此内容。
  • @Maurice 很高兴能帮上忙。如果您发布 PHP 解决方案,请确保包含足够的上下文以使其有意义;我没有尝试推荐一个,因为没有任何背景可以工作。
  • @Maurice 你不应该需要这些测试,因为它们被接下来的两个暗示:UNIX_TIMESTAMP(TIMESTAMP(date, time)) &gt;= $timestamp
猜你喜欢
  • 1970-01-01
  • 2020-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-08
  • 2016-06-28
  • 2022-01-18
相关资源
最近更新 更多