【问题标题】:SQL: Find next row in a where clause given an IDSQL:在给定 ID 的 where 子句中查找下一行
【发布时间】:2011-06-19 12:41:21
【问题描述】:

我在 MySQL 数据库中有一个名为 products 的表。 products 看起来有点像这样:

id   name     din        strength active deleted
1    APA TEST 00246374   25       1      0
4    APA BOB  00246375   50       1      0
5    APA TIRE 00246888   50       1      0
7    APA ROT  00521414   100      1      0
9    APA APA  01142124   100      1      0
6    APA CODE 00121212   150      1      0
8    APA SERV 00011145   600      1      0

显然,我遗漏了几个对我的问题不重要的专栏。当我查询这个表时,我将按几个不同的列之一进行排序(用户界面允许单个用户更改排序列和顺序),并且我可能有一个搜索子句,在这种情况下,我将执行一个 LIKE 子句名称和 DIN。

我想知道的是,给定排序信息和搜索信息,以及特定产品的 ID(假设我搜索了 004,它返回了 3 个结果,我正在查看其中一个),我怎么能得到下一个和以前的产品?

我需要这样做,因为如果用户在搜索和排序结果后点击编辑/查看其中一个产品,他们希望能够在不转到上一页的情况下循环浏览结果。

在 SQL 中是否有一种很好且有效的方法来执行此操作,或者我最好还是使用 PHP?也欢迎任何想法。

当前使用此 SQL 查询,如果我按 strength 列排序会遇到问题,因为存在重复值

SELECT T.*
FROM `wp_products` T
INNER JOIN `wp_products` curr on curr.id = 38
   AND ((T.strength = curr.strength and T.id < curr.id)
    OR (T.strength > curr.strength))
WHERE T.active = 1 AND T.deleted = 0 AND (T.name LIKE '%%' OR T.din LIKE '%%')
ORDER BY T.strength ASC, T.id ASC
LIMIT 1

我的 PHP 代码(使用 WordPress)(旨在获取下一项)

    $sql = 'SELECT T.*
FROM `' . $wpdb->prefix . 'apsi_products` T
INNER JOIN `' . $wpdb->prefix . 'apsi_products` curr on curr.id = ' . $item->id . '
   AND ((T.' . $wpdb->escape( $query['orderby'] ) . ' = curr.' . $wpdb->escape( $query['orderby'] ) . ' and T.id > curr.id)
    OR (T.' . $wpdb->escape( $query['orderby'] ) . ' > curr.' . $wpdb->escape( $query['orderby'] ) . '))
WHERE T.active = 1 AND T.deleted = 0 AND (T.name LIKE \'%' . $query['where'] . '%\' OR T.din LIKE \'%' . $query['where'] . '%\')
ORDER BY T.' . $wpdb->escape( $query['orderby'] ) . ' ' . $query['order'] . ', T.id ASC
LIMIT 1;';

【问题讨论】:

    标签: php sql mysql pagination


    【解决方案1】:

    您需要对当前记录有一个引用,然后根据排好序的列逐步查找下一条记录。下面的示例假设它是按

    排序的
    ORDER BY Active, DIN, NAME
    

    第一:

    SELECT *
    FROM TABLE
    WHERE NAME LIKE '%X%' AND DIN LIKE '%%'
    ORDER BY Active, DIN, Name
    LIMIT 1;
    

    下一步:(确保用正确的括号将 CURR.ID = 6 和 AND-OR 分开!

    SELECT *
    FROM TABLE T
    INNER JOIN TABLE CURR ON CURR.ID = 6 # the current ID being viewed
       AND ((T.Active = Curr.Active AND T.DIN = Curr.DIN AND T.NAME > Curr.Name)
         OR (T.Active = Curr.Active AND T.DIN > Curr.DIN)
         OR T.Active > Curr.Active)
    WHERE T.NAME LIKE '%X%' AND T.DIN LIKE '%%'
    ORDER BY T.Active, T.DIN, T.Name
    LIMIT 1;
    

    一个工作示例如下所示

    create table products
    (ID int, SEED int, NAME varchar(20), DIN varchar(10), ACTIVE int, DELETED int);
    insert products values
    (1,  0,    'Product #1', '004812', 1,    0),
    (2,  0,    'Product #2', '004942', 0,    0),
    (3,  0,    'Product #3', '004966', 1,    0),
    (4,  0,    'Product #4', '007437', 1,    1),
    (5,  2,    'Product #2', '004944', 0,    0),
    (6,  2,    'Product #2', '004944', 1,    0);
    
    SELECT *
    FROM products
    WHERE active = 1 AND deleted = 0
    ORDER BY din DESC, ID desc;
    
    Output:
    "ID";"SEED";"NAME";"DIN";"ACTIVE";"DELETED"
    "3";"0";"Product #3";"004966";"1";"0"
    "6";"2";"Product #2";"004944";"1";"0"
    "1";"0";"Product #1";"004812";"1";"0"
    

    如果 current 是 ID=6 的行,则可以使用检索下一条记录

    SELECT T.*
    FROM products T
    INNER JOIN products curr on curr.ID = 6
       AND ((T.din = curr.din and T.ID > curr.ID)
        OR (T.din < curr.din))
    WHERE T.active = 1 AND T.deleted = 0
    ORDER BY T.din DESC, T.ID ASC
    LIMIT 1;
    

    【讨论】:

    • 对不起,我对 SQL 一直很糟糕。我应该保留对 CURR 的引用,还是应该将 CURR 和 TABLE 更改为我的表名?
    • TABLE 是表名的占位符。 CURR 和 T 是别名 - 不要理会它们。
    • 没有您的询问,我没有成功,很可能我做错了什么。我的查询如下所示:SELECT * FROM table_name WHERE active = 1 AND deleted = 0 ORDER BY din DESC 当我运行您的查询时,我得到了查询返回的第一行,即使我使用的是返回的第 5 个 ID。我应该从第 6 个 ID 获得 ID 38。想法?
    • @Rogue - 在 AND-OR 周围添加了括号。请查看编辑后的答案
    • @cyberkiwi 非常感谢,您发布的新查询很有魅力。
    【解决方案2】:

    如果没有像memcached 这样的持久性缓存层,您可以在其中存储结果并在不重新发出查询的情况下获取它,一个简单的解决方案可以在mysql 中启用query cache。这样,如果缓存没有被其他查询失效,当你重新发出查询时,拉取结果的成本会降低

    【讨论】:

    • 只需一个查询就可以有效地完成。看我的回答
    • @cyberwiki 从我对问题的理解来看,这不是一个单一的查询问题,因为这些项目将从不同的页面请求。您的查询将解决如何查询不同的项目,但将针对每个页面请求重新评估,您同意吗?
    • 是的,但是通过适当的索引,每个限制 1 仅根据需要有效地检索 1 条记录。每个 SO 页面加载至少从数据库请求大约 10 个项目,因此往返应该是一个小问题。
    • @cyberkiwi 我同意你的观点,我喜欢你的解决方案,我们已经分析了这个问题,提出了两种不同的优化策略。无论如何,我要 +1 :)
    • +1 如果要检索的结果集很小,则可以选择持久性和缓存
    【解决方案3】:

    对于每个显示的结果,使用适当的 ID 设置 nextprevious 链接。

    无法从详细信息视图更改排序顺序。

    【讨论】:

      【解决方案4】:

      有趣的问题。我不认为你可以在一个查询中完成,但你可以用三个来完成。

      第一个查询提取答案行。诀窍是保留他们排序的列的值。假设这是“SortedColumn”,值为“M”。假设你得到的行的 id 是 10。

      您的第二个查询将与第一个相同,但将是“top 1”,并将添加到 where 子句“...and sortedColumn >= 'M' and id 10” 这将为您提供下一个排。您可能希望返回 10 行并保留它们以防止一遍又一遍地执行此操作。

      要获得前一个,请将您的排序切换为降序,并且您添加的 where 子句是“...and sortColumn 10”。这会让你上一行。同样,10 行可能对您的用户更有用。

      希望这会有所帮助。

      【讨论】:

      • 我很快就试过了,我不相信它可以工作。假设我按名称列排序。例如,name >= 'PRODUCT NAME #3' 部分似乎不起作用
      猜你喜欢
      • 1970-01-01
      • 2015-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-12
      • 2011-12-03
      • 2020-09-18
      相关资源
      最近更新 更多