【问题标题】:MySQL SELECT WHERE OR - pick one line only if second is emptyMySQL SELECT WHERE OR - 仅当第二行为空时才选择一行
【发布时间】:2016-04-26 17:08:56
【问题描述】:

我要加入 2 个表,例如

PRODUCTS (PRODUCT_ID, NAME)

PRICE_LEVELS (PRODUCT_ID, PRICE, PRICE_LVL_NAME )

(ofc。它被简化了,有几个连接)。

PRICE_LEVELS 表中,我有一些价格水平名称的可能性,比如“DEFAULT”和 LEVEL1,所以我们最终得到如下内容:

PRODUCT_ID | PRICE | PRICE_LVL_NAME
1          | 100   | _DEFAULT_
1          | 50    | LEVEL1
2          | 130   | _DEFAULT_

两个表都在视图中连接。

我需要的是获取价格,但只有一次 - 我的意思是如果定义了 LEVEL1,则选择那个,否则选择 DEFAULT。

与此同时,我使用了 GROUP BY 并且事情似乎有效,但我不知道为什么(ofc。我使用了很多测试数据,它总是有效,但不确定它的可靠性如何)。

假设我们的视图(结合两个表)的名称为 V_PRODUCTS,所以我正在运行查询:

SELECT * 
FROM `V_PRODUCTS` 
WHERE (PRICE_LVL_NAME = '_DEFAULT_' OR PRICE_LVL_NAME = 'LEVEL1') 
GROUP BY `PRODUCT_ID `;

所以问题是:

  1. 为什么上面的查询有效? GROUP BY 始终选择 LEVEL1,如果可用,如果不可用,则选择 DEFAULT。这正是我所需要的,但需要了解它为什么会这样工作。

  2. 有什么方法可以在 SQL 中更明确地做到这一点?

更新:可能的关卡数量不限

【问题讨论】:

  • 您可以加入PRICE_LEVELS 表两次;使用PRICE_LEVEL_NAME='_DEFAULT_'PRICE_LEVEL_NAME='LEVEL1' 作为连接条件。仅当缺少 1 级表中的条目时才选择默认表中的条目。
  • 不幸的是,这不是一个选项,因为“级别”是动态的,因此级别类型的数量会有所不同..
  • 所以,使用PRICE_LEVEL_NAME LIKE 'LEVEL%' 作为你的条件。
  • LEVEL1 仅作为示例,它在真实数据库中是自动递增的 :)

标签: mysql sql mariadb


【解决方案1】:

Q1:不可靠,可能是基于内部存储,随时可能发生变化。

Q2:使用 Outer Join 两次加入表并使用 COALESCE 返回最佳匹配:

SELECT ..., COALESCE(pl1.PRICE, pl2.PRICE)
FROM `V_PRODUCTS` as p 
LEFT JOIN PRICE_LEVELS as pl1
  ON p.PRODUCT_ID = pl1.PRODUCT_ID
 and PRICE_LVL_NAME = 'LEVEL1') 
LEFT JOIN PRICE_LEVELS as ply
  ON p.PRODUCT_ID = pl2.PRODUCT_ID
 and PRICE_LVL_NAME = '_DEFAULT_'

【讨论】:

  • 不幸的是,这不是一个选项,因为“级别”是动态的,因此级别类型的数量会有所不同..
【解决方案2】:

我现在没有可以使用的 MySQL,但我认为您应该只需要替换此 MSSQL 的 IS NULL 部分即可使其正常工作。

如果您在关卡中不需要 AlphaNum,或者可以添加一个 INT 的关卡字段,其中默认为 0,然后为 1,依此类推,您可以将关卡连接到自身以获得最大可用关卡以加入产品表。

    SELECT PRODUCTS.*, PriceLvl.*
    FROM [PRODUCTS] LEFT JOIN 
      (SELECT p1.*
       FROM PRICE_LEVELS p1 
            LEFT JOIN PRICE_LEVELS p2 ON 
            (p1.PRODUCT_ID = p2.PRODUCT_ID 
             AND p2.PRICE_LVL > p1.PRICE_LVL)
       WHERE p2.PRICE_LVL IS NULL) PriceLvl 
    ON PRODUCTS.PRODUCT_ID = PriceLvl.PRODUCT_ID

这需要

    PRODUCT_ID  NAME
             1  Tshirts
             2  Pants

    PRODUCT_ID  PRICE   PRICE_LVL
             1  10.00   0
             1  15.00   1
             2  40.00   0
             1  20.00   2

并给予

    PRODUCT_ID  NAME    PRODUCT_ID  PRICE   PRICE_LVL
             1  Tshirts          1  20.00   2
             2  Pants            2  40.00   0

【讨论】:

    【解决方案3】:

    我会为每个级别分配一个数值,默认为零,并将其存储在同一个表中。

    select t1.price from price_levels t1 
    where t1.product_id = @X and price_level = (select max(price_level) 
    from price_levels t2 where t1.product_id = t2.product_id)
    

    【讨论】:

      【解决方案4】:
      WHERE price_level IN ('_DEFAULT_', 'mylevel')
      ORDER BY
          price_level = '_DEFAULT_'
      LIMIT 1
      

      WHERE 将抓取 1 或 2 行。

      price_level = '_DEFAULT_' 默认为 1,否则为 0。0 排在 1 之前。因此,如果有 'mylevel',它将排在第一位。

      LIMIT 1 表示选择第一行(或唯一一行)。

      【讨论】:

      • 我对订购进行了一些测试,但不幸的是它不能这样工作
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-17
      • 2014-02-22
      • 2013-03-10
      • 1970-01-01
      • 1970-01-01
      • 2013-08-11
      • 2023-03-14
      相关资源
      最近更新 更多