【问题标题】:Filtering down specific criteria for all rows and return only one row for each column with the same value过滤所有行的特定条件,并为每列返回具有相同值的一行
【发布时间】:2020-01-18 20:22:06
【问题描述】:

我有 house_leaseshouse_lease_terms(请参阅下面的表格架构)。 house_lease 可以有多个 house_lease_terms,但一次只能有一个“有效”条款。

表定义:

CREATE TABLE `house_leases` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `house_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `house_lease_terms` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `house_lease_id` int(10) unsigned NOT NULL,
  `date_start` datetime NOT NULL,
  `date_end` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `house_lease_terms_house_lease_id_foreign` (`house_lease_id`),
  CONSTRAINT `house_lease_terms_house_lease_id_foreign` FOREIGN KEY (`house_lease_id`) REFERENCES `house_leases` (`id`) ON DELETE CASCADE
);

如您所见,house_lease_terms.house_lease_id 对应于特定的 house_lease,但是可以有多个具有相同 的行house_lease_id

确定“活跃”术语的规则是:

date_start NOW() OR date_end 为空

如果没有返回任何行,则“活动”术语必须在将来,因此规则更改为:

date_start > NOW()

如果条款不在未来,我们按 date_start DESC 排序,因为可能会返回多行,我们希望最新的 date_start 位于结果的顶部。否则,我们按 date_start ASC 排序,因为我们希望离现在最近的 date_start 位于顶部。

然后我限制为 1 以仅获得一个结果,并且该行被视为“活动”术语。如果没有返回结果,则没有“有效”字词。

我有一个 SQL 语句,它具有获取特定 house_lease_id 的逻辑。看起来像这样:

SELECT * FROM house_lease_terms
WHERE 
CASE 
    WHEN 
        (SELECT COUNT(*) FROM house_lease_terms WHERE date_start <= NOW() AND (date_end > NOW() OR date_end IS NULL) AND house_lease_id = 1)
    THEN 
        date_start <= NOW() AND (date_end > NOW() OR date_end IS NULL)
    ELSE
        date_start > NOW()
END
AND house_lease_id = 1
ORDER BY
    IF(
        (SELECT COUNT(*) FROM house_lease_terms WHERE date_start <= NOW() AND (date_end > NOW() OR date_end IS NULL) AND house_lease_id = 1), 
        unix_timestamp(date_start), 
        -unix_timestamp(date_start)
    ) DESC
LIMIT 1;

此语句有效,但我希望有更好的方法(更有效)来获取特定 house_lease_id 的“有效”条款(如果您知道更好的解决方案,请分享)。

现在我需要一个查询来获取所有不同 house_lease_id 的“有效”条款。

我不希望任何类型的自定义 MySQL 函数或存储过程来执行此操作。我不知道从哪里开始创建这个查询。我想我可以在一些子选择或连接中使用上面的查询,但我不确定我会怎么做。

任何帮助将不胜感激!

SQLFiddle 处理数据:http://sqlfiddle.com/#!9/cab159/2/0

【问题讨论】:

  • 你用的是什么版本的mysql?
  • 在 ORDER BY 中使用 CASE 而不是 WHERE。
  • 版本:5.7.24

标签: mysql sql


【解决方案1】:

寻找:

SELECT *
FROM house_lease_terms
WHERE house_lease_id = 1
  AND (   (   (    date_start <= NOW() 
               AND date_end > NOW()
              ) 
           OR date_end IS NULL
          )
       OR (date_start > NOW()
          )
      )
ORDER BY CASE WHEN ((date_start <= NOW() AND date_end > NOW()) OR date_end IS NULL)
              THEN DATEDIFF(NOW(), date_start)
              ELSE DATEDIFF(date_start, NOW()) + 1000000
              END

SELECT id,
       CASE WHEN @var2=house_lease_id 
            THEN @var1:=house_lease_id 
            ELSE 1
            END row_number_in_house_lease_id,
       @var2:=house_lease_id house_lease_id,
       date_start,
       date_end,
       created_at,
       updated_at,
       deleted_at
FROM house_lease_terms, (SELECT @var1:=0, @var2:=0) variables
WHERE house_lease_id = 1
  AND (   (   (    date_start <= NOW() 
               AND date_end > NOW()
              ) 
           OR date_end IS NULL
          )
       OR (date_start > NOW()
          )
      )
ORDER BY house_lease_id,
         CASE WHEN ((date_start <= NOW() AND date_end > NOW()) OR date_end IS NULL)
              THEN DATEDIFF(NOW(), date_start)
              ELSE DATEDIFF(date_start, NOW()) + 1000000
              END

下一代。

SELECT id, house_lease_id, date_start, date_end
FROM (
SELECT id,
       CASE WHEN @var2=house_lease_id 
            THEN @var1:=@var1+1 
            ELSE @var1:=1
            END row_number_in_house_lease_id,
       @var2:=house_lease_id house_lease_id,
       date_start,
       date_end
FROM house_lease_terms, (SELECT @var1:=0, @var2:=0) variables 
WHERE (   (   (    date_start <= NOW() 
               AND date_end > NOW()
              ) 
           OR date_end IS NULL
          )
       OR (date_start > NOW()
          )
      )
ORDER BY house_lease_id,
         CASE WHEN ((date_start <= NOW() AND date_end > NOW()) OR date_end IS NULL)
              THEN DATEDIFF(NOW(), date_start)
              ELSE DATEDIFF(date_start, NOW()) + 1000000
              END
) AS cte
WHERE row_number_in_house_lease_id = 1;

fiddle

【讨论】:

  • 成功了!所以我现在有一个更优化的查询来获取特定的 house_lease_id,但仍然需要帮助来获取所有 house_lease_id 的“活动”条款
  • “订单条款”中的未知列“house_lease_terms”。我也可以取出 house_lease_id = 1 并获取所有有效的房屋租赁条款吗?我只想为每个 house_lease_id 返回一行
  • @JacobHyde 已更正。
  • 我可以取出 house_lease_id = 1 并获取所有有效的房屋租赁条款吗?我只想为每个 house_lease_id 返回一行
  • 将最后一个查询用作 CTE(对于 MySQL 8+)或子查询。选择您需要的带有_house_lease_idrow_number_in_house_lease_id 的记录。
【解决方案2】:

我不确定这是否是您认为干净的,但似乎任何解决此问题的方法都会导致相当复杂和混乱的查询。

根据您的原始查询:

SELECT house_lease_id, 
       SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY
                                    IF(
                                        (SELECT COUNT(*) FROM house_lease_terms AS hlt2 
                                         WHERE date_start <= NOW() 
                                         AND (date_end > NOW() OR date_end IS NULL) 
                                         AND hlt2.house_lease_id = hlt.house_lease_id), 
                                         unix_timestamp(date_start), 
                                         -unix_timestamp(date_start)
                                    ) DESC),',',1) AS id,
       SUBSTRING_INDEX(GROUP_CONCAT(date_start ORDER BY
                                    IF(
                                        (SELECT COUNT(*) FROM house_lease_terms AS hlt3 
                                         WHERE date_start <= NOW() 
                                         AND (date_end > NOW() OR date_end IS NULL) 
                                         AND hlt3.house_lease_id = hlt.house_lease_id), 
                                         unix_timestamp(date_start), 
                                         -unix_timestamp(date_start)
                                    ) DESC),',',1) AS date_start,
       SUBSTRING_INDEX(GROUP_CONCAT(date_end ORDER BY
                                    IF(
                                        (SELECT COUNT(*) FROM house_lease_terms AS hlt4 
                                         WHERE date_start <= NOW() 
                                         AND (date_end > NOW() OR date_end IS NULL) 
                                         AND hlt4.house_lease_id = hlt.house_lease_id), 
                                         unix_timestamp(date_start), 
                                         -unix_timestamp(date_start)
                                    ) DESC),',',1) AS date_end 
FROM house_lease_terms AS hlt
WHERE 
CASE 
    WHEN 
        (SELECT COUNT(*) FROM house_lease_terms AS hlt5 WHERE date_start <= NOW() 
         AND (date_end > NOW() OR date_end IS NULL) 
         AND hlt5.house_lease_id = hlt.house_lease_id)
    THEN 
        date_start <= NOW() AND (date_end > NOW() OR date_end IS NULL)
    ELSE
        date_start > NOW()
END
GROUP BY hlt.house_lease_id;

应该可以的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    • 2022-01-27
    • 1970-01-01
    相关资源
    最近更新 更多