【问题标题】:Using CASE for both field and order sorting in an ORDER BY clause with IN statement在带有 IN 语句的 ORDER BY 子句中使用 CASE 进行字段和顺序排序
【发布时间】:2021-05-28 06:21:45
【问题描述】:

我目前在 Prisma 工作并尝试使用原始 sql 查询进行排序,但我无法做到这一点。我对字段名称和排序顺序都使用变量,我还想包含 IN 语句以缩短代码量(没有多个 WHEN 语句)和 ELSE 语句作为默认值:

const users = await prisma.$queryRaw<User[]>(
        `
        SELECT * FROM "Users"
        ORDER BY 
            CASE WHEN $1 IN ('name','surname') THEN $1 ELSE 'id' END
            CASE WHEN $2 IN ('ASC','DESC') THEN $2 ELSE 'ASC' END
        `,
        sort?.fieldName || 'name',
        sort?.order || 'ASC'
    )

执行这行代码时出现此错误:

 "code": "P2010",
          "clientVersion": "2.17.0",
          "meta": {
            "code": "42601",
            "message": "db error: ERROR: syntax error at or near \"CASE\""
          },
          "stacktrace": [
            "Error: ",
            "Invalid `prisma.queryRaw()` invocation:",
            "",
            "",
            "  Raw query failed. Code: `42601`. Message: `db error: ERROR: syntax error at or near \"CASE\"`",
            "    at cb (/app/node_modules/@prisma/client/runtime/index.js:78689:17)",
            "    at processTicksAndRejections (internal/process/task_queues.js:93:5)"
          ],
          "message": "\nInvalid `prisma.queryRaw()` invocation:\n\n\n  Raw query failed. Code: `42601`. Message: `db error: ERROR: syntax error at or near \"CASE\"`",

我试图通过在很多变体中大量组合逗号、括号来实现这一点,但充其量算法只是不排序记录。

我也直接在数据库上执行了类似的命令(没有参数但具有实际值)但结果相同。

编辑 1

我为测试目的准备了 SQL 查询,但没有参数(我不知道如何在原始 SQL 中使用它)。

SELECT * FROM "Users"
        ORDER BY 
            CASE WHEN 'name' IN ('name','surname') THEN 'name' ELSE 'id' END,
            CASE WHEN 'DESC' IN ('ASC','DESC') THEN 'DESC' ELSE 'ASC' END

以上代码执行正常,但排序仍然不正确。

【问题讨论】:

  • 第二个 case 表达式前需要一个逗号。
  • 所以你的意思是 - THEN $1 ELSE 'id' END, ..... ?
  • 它正在正确执行,但是当我将“名称”传递给排序字段时,它仍然按 id 排序。所以看起来像查询选择 ELSE 语句。
  • 请检索正在执行的有效 SQL 并将其添加到问题中。您的代码可能产生了错误的 SQL 查询。
  • 我已将 SQL 查询添加到问题中

标签: sql postgresql prisma


【解决方案1】:

您正在使用一种编程语言(这是 PHP 吗?)从变量创建 SQL 语句。生成的 SQL 可能如下所示(如果您的变量包含单引号):

SELECT * FROM "Users"
ORDER BY 
    CASE WHEN 'name' IN ('name','surname') THEN 'name' ELSE 'id' END,
    CASE WHEN 'DESC' IN ('ASC','DESC') THEN 'DESC' ELSE 'ASC' END

...或像这样(如果您的变量不包含单引号):

SELECT * FROM "Users"
ORDER BY 
    CASE WHEN name IN ('name','surname') THEN name ELSE id END,
    CASE WHEN DESC IN ('ASC','DESC') THEN DESC ELSE 'ASC' END

第一个查询根本不进行排序。您可以通过常量字符串 'name' 和 'DESC' 选择行。由于每一行的字符串都相同,因此不会进行排序。

第二个查询失败并出现错误。 ORDER BY 子句本质上变成了ORDER BY name, DESC,但表中没有DESC 列。您也不能删除逗号,因为您有一个无效的CASE 表达式。这里的主要问题是:CASE 表达式的结果不能是 SQL 关键字。

两种选择:

选项 1:完全使用您的编程语言构建查询。大致如下:

$column = ($1 = "name" or $1 = "surname") ? $1 : "id";
$order = ($2 = "DESC") ? "DESC" : "ASC";
$query = `SELECT * FROM "Users" ORDER BY $column $order`;

选项 2:让您的 SQL 处理这个问题:

$query = `
  SELECT * FROM "Users" 
  ORDER BY 
    CASE WHEN '$2' = 'DESC' THEN
      CASE WHEN '$1' = 'name' THEN name
           WHEN '$1' = 'surname' THEN surname
           ELSE id
      END
    ELSE
      ''
    END DESC,
    CASE WHEN '$2' <> 'DESC' THEN
      CASE WHEN '$1' = 'name' THEN name
           WHEN '$1' = 'surname' THEN surname
           ELSE id
      END
    ELSE
      ''
    END ASC
  `;

还有最后一个问题。 CASE 表达式中的结果必须是一种类型。我想namesurname 是字符串,所以如果id 是数字,那么你必须将它转换成字符串。例如。将id 替换为TO_CHAR(id, '000000000000000000')

【讨论】:

  • 我正在使用 NodeJS 和 ORM,称为 Prisma
  • 您自己编写 SQL 语句的 ORM?无论如何,使我的解决方案之一适应您的环境。这应该没有太大区别。
  • 您提供的示例,不能在 SQL 和代码中工作。
  • 我已经在原始数据库上尝试过这个查询,通过将参数 '$1' 和 '$2' 更改为实际值,因为 postgres 没有参数。但是还是不行。
【解决方案2】:
const users = await prisma.$queryRaw<User[]>(
        `
        SELECT * FROM "Users"
        ORDER BY 
            CASE WHEN $1 = 'true' THEN
                CASE $2 WHEN 'name' THEN u.name
                WHEN 'surname' THEN u.surname
                ELSE u.id::varchar END
            END DESC,
            CASE WHEN $1 = 'false' THEN
                CASE $2 WHEN 'name' THEN u.name 
                WHEN 'surname' THEN u.surname
                ELSE u.id::varchar END
            END ASC
        `,
       sort?.order === 'DESC' ? 'true' : 'false',
       sort?.fieldName || 'id'
    )

这个查询对我有用,不幸的是我不知道如何将它与IN 语句一起使用。 使用u.id::varchar 是因为CASE 语句必须有一个类型返回,ID 字段是Int,所以我将类型更改为VARCHAR

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-20
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    相关资源
    最近更新 更多