计算包含字母/数字的行数

Geo*_*rge 3 sql postgresql count aggregate-functions

我想要实现的是直截了当的,但是它有点难以解释,我不知道它是否实际上甚至可能在postgres中.我处于相当基础的水平.SELECT, FROM, WHERE, LEFT JOIN ON, HAVING等基本的东西.

我试图计算包含特定字母/数字的行数,并根据字母/数字显示该计数.

即多少行包含包含"a/A"的条目(不区分大小写)

我要查询的表格是电影名称列表.我想做的就是分组并计算'az'和'0-9'并输出总数.我可以顺序运行36个查询:

SELECT filmname FROM films WHERE filmname ilike '%a%'
SELECT filmname FROM films WHERE filmname ilike '%b%'
SELECT filmname FROM films WHERE filmname ilike '%c%'
Run Code Online (Sandbox Code Playgroud)

然后在结果上运行pg_num_rows以查找我需要的数字,依此类推.

我知道这是多么密集和ilike更多所以我宁愿避免这样.虽然数据(下面)在数据中有大小写,但我希望结果集不区分大小写.即"盯着山羊的男人"a/A,t/T和s/S对于结果集不会计算两次.我可以将表复制到辅助工作表,其中数据全部是strtolower,如果它使查询更简单或更容易构造,则处理查询的数据集.

一个替代方案可能是

SELECT sum(length(regexp_replace(filmname, '[^X|^x]', '', 'g'))) FROM films;

每个字母组合,但再次36个查询,36个数据集,我更喜欢,如果我可以在单个查询中获取数据.

这是我的一组14部电影的短数据集(实际上包含275行)

District 9
Surrogates
The Invention Of Lying
Pandorum
UP
The Soloist
Cloudy With A Chance Of Meatballs
The Imaginarium of Doctor Parnassus
Cirque du Freak: The Vampires Assistant
Zombieland
9
The Men Who Stare At Goats
A Christmas Carol
Paranormal Activity
Run Code Online (Sandbox Code Playgroud)

如果我在一列中手动布置每个字母和数字然后注册,如果该字母出现在电影标题中,在该列中给它一个x,然后计算它们以产生总数,我会在下面有这样的东西.x的每个垂直列都是该电影名称中的字母列表,无论该字母出现的次数或其大小写.

以上简短结果的结果是:

A  x x  xxxx xxx  9 
B       x  x      2 
C x     xxx   xx  6
D x  x  xxxx      6
E  xx  xxxxx x    8
F   x   xxx       4 
G  xx    x   x    4
H   x  xxxx  xx   7
I x x  xxxxx  xx  9
J                 0
K         x       0
L   x  xx  x  xx  6
M    x  xxxx xxx  8
N   xx  xxxx x x  8
O  xxx xxx x xxx  10
P    xx  xx    x  5
Q         x       1
R xx x   xx  xxx  7
S xx   xxxx  xx   8
T xxx  xxxx  xxx  10
U  x xx xxx       6
V   x     x    x  3
W       x    x    2
X                 0 
Y   x   x      x  3
Z          x      1 
0                 0  
1                 0  
2                 0 
3                 0
4                 0
5                 0
6                 0
7                 0
8                 0
9 x         x     1
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,每列都是"电影名称".如您所见,第5列仅标记"u"和"p",第11列仅标记"9".最后一列是每个字母的标记.

我想以某种方式构建一个查询给我结果行:A 9,B 2,C 6,D 6,E 8等考虑从我的电影专栏中提取的每一行条目.如果那封信没有出现在任何一行中我想要零.

我不知道这是否可能,或者是否在php中系统地进行36次查询是唯一的可能性.

在当前数据集中,有275个条目,每月增长约8.33(一年100个).我预测它到2019年将达到1000行左右,到那时我毫无疑问会使用一个完全不同的系统,所以我不需要担心使用庞大的数据集进行搜索.

目前最长的标题是"Percy Jackson&the Olympians:The Lightning Thief",50个字符(是的,我知道的电影很差;-),最短的是1,"9".

我正在运行Postgres的9.0.0版本.

抱歉,如果我以多种方式多次说同样的话,我试图获取尽可能多的信息,以便你知道我想要实现的目标.

如果您需要任何澄清或更大的数据集来测试,请询问,我将根据需要进行编辑.

建议非常欢迎.

编辑1

Erwin感谢您的编辑/标签/建议.同意他们所有.

修正了Erwin建议丢失的"9"拼写错误.手动转录我的错误.

kgrittn,感谢您的建议,但我无法从9.0.0更新版本.我问过我的提供商他们是否会尝试更新.

响应

感谢Erwin的出色回复

对延迟回复表示抱歉,但我一直在尝试让您的查询工作并学习新关键字以了解您创建的查询.

我调整了查询​​以适应我的表结构,但结果集不是预期的(全为零)所以我直接复制了你的行并得到了相同的结果.

虽然两种情况下的结果集都列出了具有适当字母/数字的所有36行,但是所有行都显示零作为计数(ct).

我试图解构查询以查看它可能会落空的地方.

的结果

SELECT DISTINCT id, unnest(string_to_array(lower(film), NULL)) AS letter
FROM  films
Run Code Online (Sandbox Code Playgroud)


是"没有找到行".也许它应该从更广泛的查询中提取出来,我不确定.

当我删除了不需要的函数时,结果是14行都是"NULL"

如果我调整功能

COALESCE(y.ct, 0) to COALESCE(y.ct, 4)<br />
Run Code Online (Sandbox Code Playgroud)

然后我的数据集响应所有4的每个字母而不是零,如前所述.

简要地读了COALESCE,"4"是替代值,我猜y.ct是NULL并用第二个值代替(这是为了覆盖序列中字母不匹配的行,即如果没有电影包含'q'然后'q'列将具有零值而不是NULL?)

我试过的数据库是SQL_ASCII,我想知道这是不是一个问题,但我在一个运行版本8.4.0与UTF-8上有相同的结果.

如果我犯了一个明显的错误,但我无法返回我需要的数据集,请道歉.

有什么想法吗?

再次,感谢您的详细回复和您的解释.

Erw*_*ter 6

这个查询应该完成这项工作:

测试用例:

CREATE TEMP TABLE films (id serial, film text);
INSERT INTO films (film) VALUES
 ('District 9')
,('Surrogates')
,('The Invention Of Lying')
,('Pandorum')
,('UP')
,('The Soloist')
,('Cloudy With A Chance Of Meatballs')
,('The Imaginarium of Doctor Parnassus')
,('Cirque du Freak: The Vampires Assistant')
,('Zombieland')
,('9')
,('The Men Who Stare At Goats')
,('A Christmas Carol')
,('Paranormal Activity');
Run Code Online (Sandbox Code Playgroud)

查询:

SELECT l.letter, COALESCE(y.ct, 0) AS ct
FROM  (
    SELECT chr(generate_series(97, 122)) AS letter  -- a-z in UTF8!
    UNION ALL
    SELECT generate_series(0, 9)::text              -- 0-9
    ) l
LEFT JOIN (
    SELECT letter, count(id) AS ct
    FROM  (
        SELECT DISTINCT  -- count film once per letter
               id, unnest(string_to_array(lower(film), NULL)) AS letter
        FROM   films
        ) x
    GROUP  BY 1
    ) y  USING (letter)
ORDER  BY 1;
Run Code Online (Sandbox Code Playgroud)

更改string_to_array(),以便NULL分隔符将字符串拆分为字符(Pavel Stehule)

以前,这返回了一个空值.

  • 您可以使用regexp_split_to_table(lower(film), ''),而不是unnest(string_to_array(lower(film), NULL))(在9.1之前的版本中工作!),但它通常有点慢,并且使用长字符串会降低性能.

  • generate_series()用来生成[a-z0-9]各行.并且LEFT JOIN到查询,因此每个字母都在结果中表示.

  • 使用DISTINCT一次计算每部电影.

  • 永远不用担心1000行.这就是现代硬件上现代PostgreSQL的花生.