SQL选择数字范围


19

我发现很难获得一定数量的数字作为行中的数字MySQL

例如,范围1-5是通过以下方式实现的:

SELECT 1 
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5

将导致:

1
2
3
4
5

对于0-99,我可以交叉连接两个0-9表:

CREATE TABLE nums as
SELECT 0 as num
UNION
SELECT 1 
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 6 
UNION
SELECT 7
UNION
SELECT 8
UNION
SELECT 9
;

Select n.num*10+nums.num v 
From nums n cross join nums

我已经厌倦了编写所有这些UNION代码,并正在寻找一种缩减代码的方法。

任何想法如何高尔夫球它(例如0-1,000,000范围)在MySQL或任何SQL语法?

加分给:

  • 单一陈述
  • 没有程序
  • 没有变数
  • 没有DDL语句
  • 仅DQL语句

2
不知道这是属于meta还是dba.stackexchange.com或SQL线程中的Golfing提示
BradC

8
关闭选民:这是一个当下的挑战;与高尔夫规范无关的不是挑战的问题被视为主题技巧问题。
HyperNeutrino

3
我有点喜欢SO的答案。充其量只不过是骇人听闻的,但您毕竟要求打高尔夫球。
Arnauld

@Arnauld太神奇了!
Dimgold

2
如果“任何SQL”包括PostgreSQL,请参见generate_series()。这里有几个用法示例
manatwork

Answers:


9

对于支持递归CTE的 SQL方言(如sqlite),您可以执行以下操作:

WITH RECURSIVE f(x) AS
(
  SELECT 1 UNION ALL SELECT x + 1 FROM f LIMIT 1000000
)
SELECT x
FROM f;

这不依赖于任何现有表,您可以根据需要更改LIMIT子句。我最初在StackOverflow上看到了这种形式。


2
优秀。这是可以在MS SQL中使用的高级版本:WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t 对于不同的端点,只需将1和更改为所需的值36即可。
BradC

1
糟糕,如果您希望在MS SQL中包含100多个行,则可能需要在option (maxrecursion 0)上述语句的末尾添加,否则递归超过100就会出错。(可以将其设置maxrecursion为特定值,或者将其设置为0以允许无限) 。
BradC

6

类似于@BradC的方法

我使用了MS SQL,该表的表[master]范围为-1到2048。您可以使用BETWEEN运算符来创建范围。

SELECT DISTINCT(number)
FROM master..[spt_values] 
WHERE number BETWEEN 1 AND 5

如果您想打高尔夫球,可以这样做:

SELECT TOP 5 ROW_NUMBER()OVER(ORDER BY number)FROM master..spt_values

1
对于高尔夫,您可以使用WHERE number>0AND number<21
BradC

你为什么要使用distinct?似乎多余。
Magic Octopus Urn

1
@MagicOctopusUrn因为该表中有重复的数字。
奥利弗(Oliver)

1
是的,您需要使用DISTINCT或使用WHERE type ='P'。区别稍短。
BradC

1
@BradC,或SELECT DISTINCT(number+2)... WHERE number<19
Peter Taylor


4

这篇文章的不错选择(由@Arnauld找到):

SELECT id%1000001 as num
FROM <any_large_table>
GROUP BY num

对我来说,这几乎可以解决挑战。


这似乎依赖于现有表,该表已经具有id通过很大的值填充的字段。如此特定的数据库,如果有人删除了产品ID = 4021,您可能会错过一行。–
BradC

是的,但是对于相对较小的范围(几天为1-7,几个月为1-12等)来说确实不错,
Dimgold

4

特定于PostgreSQL

generate_series()生成一个集合,因此您不仅可以在from子句中使用它,还可以在可能出现集合的任何地方使用它:

psql=# select generate_series(10, 20, 3);
 generate_series 
-----------------
              10
              13
              16
              19
(4 rows)

您还可以直接在集合上执行操作:

psql=# select 2000 + generate_series(10, 20, 3) * 2;
 ?column? 
----------
     2020
     2026
     2032
     2038
(4 rows)

如果多个集合的长度相同,则可以并行遍历它们:

psql=# select generate_series(1, 3), generate_series(4, 6);
 generate_series | generate_series 
-----------------+-----------------
               1 |               4
               2 |               5
               3 |               6
(3 rows)

对于具有不同长度的集合,将生成笛卡尔乘积:

psql=# select generate_series(1, 3), generate_series(4, 5);
 generate_series | generate_series 
-----------------+-----------------
               1 |               4
               2 |               5
               3 |               4
               1 |               5
               2 |               4
               3 |               5
(6 rows)

但是,如果在from子句中使用它们,则也将获得相同长度集的笛卡尔积:

psql=# select * from generate_series(1, 2), generate_series(3, 4) second;
 generate_series | second 
-----------------+--------
               1 |      3
               1 |      4
               2 |      3
               2 |      4
(4 rows)

它还可以生成一组时间戳。例如,您出生于2000-06-30,想知道您在哪个年份庆祝周末生日:

psql=# select to_char(generate_series, 'YYYY - Day') from generate_series('2000-06-30', current_date, interval '1 year') where to_char(generate_series, 'D') in ('1', '7');
     to_char      
------------------
 2001 - Saturday 
 2002 - Sunday   
 2007 - Saturday 
 2012 - Saturday 
 2013 - Sunday   
(5 rows)

3

MS SQL在master数据库中有一个未公开的系统表,称为spt_values。除其他外,它包含从0到2047的数字范围:

--returns 0 to 2,047
SELECT number n 
FROM master..spt_values
WHERE TYPE='P'

单独用作数字表很有用,但是在CTE中,您可以很快获得一些大数字:

--returns 0 to 4,194,304
WITH x AS(SELECT number n FROM master..spt_values WHERE TYPE='P')
SELECT 2048*x.a+*y.a
FROM x,x y
ORDER BY 1

3

(这些工作在MS-SQL中,不确定它们是否适用于mySQL或其他平台。)

对于较小的集合(有序或无序),请使用VALUES构造函数:

--Generates 0-9
SELECT a 
FROM(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9))x(a)

(这对任何东西都有效,尽管使用所有重复的单引号可使字符串变得很长。)

然后,您可以使用命名的CTE(公用表表达式)进行交叉乘法,因此不必重复此操作:

--Generates 0-999
WITH x AS(SELECT a FROM(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9))x(a))
SELECT 100*x.a+10*y.a+z.a 
FROM x,x y,x z
ORDER BY 1

尽管还有大多数技术并未针对高尔夫进行优化,但仍有大量其他技术可以使用“ SQL生成数字表”。


1
可以与a配合使用limit Y以创建任意范围吗?

1
@Rod在MS-SQL中,您必须使用SELECT TOP 250 ...
BradC

哦,我没有看到MSSQL标头= X
Rod Rod

在MySQL上不起作用,但仍然有用:)
Dimgold

2

另一个选项,此选项特定于MS SQL 2016及更高版本:

SELECT value v
FROM STRING_SPLIT('1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16', ',')

对于字符串列表,我可能会觉得更方便,但是我可以看到它对数字也很有用。


2

T-SQL,98个字节

WITH H AS(SELECT 0i UNION ALL SELECT i+1FROM H WHERE i<99)SELECT H.i+1e4*A.i+B.i*1e2FROM H,H A,H B
  • ✓单一陈述
  • ✓无程序
  • ✓没有变量
  • ✓没有DDL语句
  • ✓仅DQL语句

这是langelgjm的答案的一个很好的T-SQL版本。指数也是一个巧妙的把戏。
BradC

1

SQL Server的另一个...

WITH 
    cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),   -- 10
    cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),                             -- 100
    cte_Tally (n) AS (
        SELECT TOP (<how many ROWS do you want?>)
            ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
        FROM
            cte_n2 a CROSS JOIN cte_n2 b                                                    -- 10,000
        )
SELECT 
    t.n
FROM
    cte_Tally t;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.