【问题标题】:select one row multiple time when using IN()使用 IN() 时多次选择一行
【发布时间】:2016-05-27 17:07:37
【问题描述】:

我有这个问题:

select 
     name 
from 
     provinces 
WHERE 
     province_id IN(1,3,2,1)  
ORDER BY FIELD(province_id, 1,3,2,1)

IN() 中值的个数是动态的


如何使用给定的 ORDER BY 获取所有行甚至重复(在本例中 -> 1 )?

结果应该是这样的:

name1
name3
name2
name1

plus 我不应该使用 UNION ALL :

select * from provinces WHERE province_id=1
UNION ALL
select * from provinces WHERE province_id=3
UNION ALL
select * from provinces WHERE province_id=2
UNION ALL
select * from provinces WHERE province_id=1

【问题讨论】:

  • 为什么要在 Province_id = 1 上重复?
  • 什么数据库引擎?
  • 使用union all有什么问题?我们应该知道您不想使用的任何其他子句吗?
  • 将值放入表中,然后加入该表。
  • @MarshallTigerus 它实际上是一条路径,所以我需要分别获取名称

标签: mysql sql


【解决方案1】:

您需要一个辅助表。在 SQL Server 上可能类似于:

SELECT name
FROM (Values (1),(3),(2),(1)) As list (id) --< List of values to join to as a table
INNER JOIN provinces ON province_id = list.id

更新:在MySQL Split Comma Separated String Into Temp Table 中可用于将字符串参数拆分为辅助表。

【讨论】:

    【解决方案2】:

    要多次获取同一行,您需要加入另一个表。我建议只创建一次(!)一个辅助表。该表将仅包含一系列自然数(1、2、3、4,...等)。这样的表格可用于许多其他用途。

    这是创建它的脚本:

    create table seq (num int);
    insert into seq values (1),(2),(3),(4),(5),(6),(7),(8);
    insert into seq select num+8  from seq;
    insert into seq select num+16 from seq;
    insert into seq select num+32 from seq;
    insert into seq select num+64 from seq;
    /* continue doubling the number of records until you feel you have enough */
    

    对于手头的任务,没有必要添加很多记录,因为您只需要确保您的in 条件中的重复次数永远不会超过上述seq 表中的重复次数。我想 128 就足够了,但可以随意将记录数翻倍。

    一旦你有了上面的,你可以写这样的查询:

    select     province_id,
               name,
               @pos := instr(@in2 := insert(@in2, @pos+1, 1, '#'), 
                             concat(',',province_id,',')) ord
    from       (select @in := '0,1,2,3,1,0', @in2 := @in, @pos := 10000) init
    inner join provinces 
            on find_in_set(province_id, @in)
    inner join seq
            on num <= length(replace(@in, concat(',',province_id,','), 
                                          concat(',+',province_id,',')))-length(@in)
    order by   ord asc
    

    样本数据和样本in列表的输出:

    | province_id |   name | ord |
    |-------------|--------|-----|
    |           1 | name 1 |   2 |
    |           2 | name 2 |   4 |
    |           3 | name 3 |   6 |
    |           1 | name 1 |   8 |
    

    SQL Fiddle

    工作原理

    您需要将值列表放入变量@in 的赋值中。为了让它工作,每个有效的 id 都必须用逗号括起来,这就是为什么开头和结尾都有一个虚拟零的原因。

    通过加入seq 表,结果集可以增长。从seq 加入的特定省份记录的记录数等于列表@in 中相应province_id 的出现次数。

    没有开箱即用的函数来计算此类出现的次数,因此num &lt;= 右侧的表达式可能看起来有点复杂。但它只是为@in 中的每个匹配添加一个字符,并检查该操作增加了多少长度。这种增长就是出现的次数。

    select子句中,返回province_id@in列表中的位置,用于对结果集进行排序,因此与@in列表中的顺序相对应。其实这个位置是参照@in2取的,是@in的一个副本,但是允许改变:

    在计算此@pos 时,@in2 中先前找到的@pos 处的数字被# 字符破坏,因此无法在同一位置再次找到相同的province_id

    【讨论】:

      【解决方案3】:

      不清楚您到底想要什么,但这就是为什么它不能按您想要的方式工作。 IN 关键字是创建语句的简写,例如 ....Where_province_id = 1 OR_province_id = 2 OR_province_id = 3 OR_province_id = 1。由于在该语句的开头,province_id = 1 被评估为真,它不会不管以后再包含它,它已经是真的了。这与结果是否返回重复无关。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-31
        • 2017-11-28
        • 1970-01-01
        • 1970-01-01
        • 2011-04-02
        • 1970-01-01
        • 2017-11-08
        相关资源
        最近更新 更多