3

私が達成しようとしていることは簡単ですが、説明するのは少し難しく、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%'

次に、結果に対してpg_num_rowsを実行して、必要な数を見つけます。

私は好きがどれほど集中的で、私がもっと好きかを知っているので、それを避けたいと思います。データ(下)のデータには大文字と小文字が含まれていますが、結果セットでは大文字と小文字を区別しないようにする必要があります。つまり、「ヤギを見つめる男性」のa / A、t/Tおよびs/Sは、結果セットに2回カウントされません。テーブルをセカンダリ作業テーブルに複製できます。データはすべて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

各文字と数字を手動で列に配置し、その文字が映画のタイトルに表示されるかどうかをその列に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

上記の例では、各列は「フィルム名」です。ご覧のとおり、列5は「u」と「p」のみをマークし、列11は「9」のみをマークします。最後の列は、各文字の集計です。

映画の列から抽出されたすべての行エントリを考慮に入れて、結果の行を取得するクエリを作成したいと思います。A9、B 2、C 6、D 6、E8など。その文字がどの行にも表示されない場合は、ゼロが必要です。

これが可能かどうか、または36のクエリを使用してPHPで体系的に実行することが唯一の可能性であるかどうかはわかりません。

現在のデータセットには275のエントリがあり、月に約8.33(年に100)増加します。2019年までに約1000行に達すると予測しています。そのときまでに、まったく異なるシステムを使用することは間違いないので、トロールするために巨大なデータセットを操作することを心配する必要はありません。

現在の最長のタイトルは「パーシージャクソンとオリンピック選手:ライトニングシーフ」で、50文字(はい、私が知っている貧弱な映画;-)で、最短のタイトルは1、「9」です。

Postgresのバージョン9.0.0を実行しています。

申し訳ありませんが、同じことを何度も何度も言いましたが、私が何を達成しようとしているのかがわかるように、できるだけ多くの情報を入手しようとしています。

テストするための説明やより大きなデータセットが必要な場合は、質問してください。必要に応じて編集します。

提案は大歓迎です。

編集1

アーウィン編集/タグ/提案をありがとう。それらすべてに同意します。

Erwinによって提案された欠落している「9」タイプミスを修正しました。私の側の手動転記エラー。

kgrittn、提案ありがとうございますが、バージョンを9.0.0から更新できません。プロバイダーに更新を試みるかどうかを尋ねました。

応答

素晴らしい返事をありがとうアーウィン

応答が遅れたことをお詫びしますが、クエリを機能させ、作成したクエリを理解するための新しいキーワードを学習しようとしています。

テーブル構造に適応するようにクエリを調整しましたが、結果セットが期待どおりではなかったため(すべてゼロ)、行を直接コピーして同じ結果が得られました。

どちらの場合も、結果セットには適切な文字/数字を含む36行すべてがリストされますが、すべての行はカウント(ct)としてゼロを示します。

クエリを分解して、どこにフォールオーバーする可能性があるかを確認しようとしました。

結果として

SELECT DISTINCT id, unnest(string_to_array(lower(film), NULL)) AS letter
FROM  films


「行が見つかりません」です。おそらく、より広いクエリから抽出されたときにそうすべきであるかどうかはわかりません。

最悪の関数を削除すると、結果はすべて「NULL」の14行になりました。

機能を調整すれば

COALESCE(y.ct, 0) to COALESCE(y.ct, 4)<br />

次に、私のデータセットは、前に説明したように、ゼロではなく、すべての文字に対して4ですべて応答します。

COALESCEを簡単に読んだ後、「4」が代替値であると推測しています。y.ctはNULLであり、この2番目の値に置き換えられています(これは、シーケンス内の文字が一致しない行をカバーするためです。つまり、フィルムがない場合です。 'q'が含まれている場合、'q'列の値はNULLではなくゼロになりますか?)

これを試したデータベースはSQL_ASCIIで、それがどういうわけか問題があるのではないかと思いましたが、UTF-8を使用してバージョン8.4.0を実行している場合も同じ結果になりました。

明らかな間違いを犯したが、必要なデータセットを返すことができない場合は、お詫び申し上げます。

何かご意見は?

繰り返しになりますが、詳細な回答と説明に感謝します。

4

4 に答える 4

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');

クエリ:

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;

string_to_array()を変更して、NULLセパレーターが文字列を文字に分割するようにします(Pavel Stehule)

以前は、これはnull値を返しました。

  • regexp_split_to_table(lower(film), '')(9.1より前のバージョンで動作します!)の代わりにを使用できますがunnest(string_to_array(lower(film), NULL))、通常は少し遅く、長い​​文字列を使用するとパフォーマンスが低下します。

  • を個別の行としてgenerate_series()作成するために使用します。[a-z0-9]そして、クエリにLEFT JOINを実行すると、すべての文字が結果に表示されます。

  • DISTINCTすべてのフィルムを1回カウントするために使用します。

  • 1000行について心配する必要はありません。これは、現代のハードウェア上の現代のPostgreSQLのピーナッツです。

于 2012-05-10T16:33:32.623 に答える
0

単一のテーブルスキャンのみを必要とする非常に単純なソリューションは、次のようになります。

SELECT 
    'a', SUM( (title ILIKE '%a%')::integer),
    'b', SUM( (title ILIKE '%b%')::integer),
    'c', SUM( (title ILIKE '%c%')::integer)
FROM film

私はあなたのためにタイピングの練習として他の33文字を残しました:)

ところで、postgresqlデータベースの場合、1000行はごくわずかです。DBがサーバーのメモリよりも大きくなると、大きくなり始めます。

編集:より良いアイデアがありました

SELECT chars.c, COUNT(title)
FROM (VALUES ('a'), ('b'), ('c')) as chars(c)
    LEFT JOIN film ON title ILIKE ('%' || chars.c || '%')
GROUP BY chars.c
ORDER BY chars.c 

(VALUES('a')、('b')、('c'))as chars(c)の部分を、関心のある文字のリストを含むテーブルへの参照に置き換えることもできます。

于 2012-05-10T16:27:36.917 に答える
0

これにより、一致する文字と数字ごとに1つの列が含まれる、単一の行の結果が得られます。

SELECT
  SUM(CASE WHEN POSITION('a' IN filmname) > 0 THEN 1 ELSE 0 END) AS "A",
  SUM(CASE WHEN POSITION('b' IN filmname) > 0 THEN 1 ELSE 0 END) AS "B",
  SUM(CASE WHEN POSITION('c' IN filmname) > 0 THEN 1 ELSE 0 END) AS "C",
  ...
  SUM(CASE WHEN POSITION('z' IN filmname) > 0 THEN 1 ELSE 0 END) AS "Z",
  SUM(CASE WHEN POSITION('0' IN filmname) > 0 THEN 1 ELSE 0 END) AS "0",
  SUM(CASE WHEN POSITION('1' IN filmname) > 0 THEN 1 ELSE 0 END) AS "1",
  ...
  SUM(CASE WHEN POSITION('9' IN filmname) > 0 THEN 1 ELSE 0 END) AS "9"
FROM films;
于 2012-05-10T16:45:59.013 に答える
0

Erwinsと同様のアプローチですが、長期的にはより快適かもしれません。

興味のある各キャラクターでテーブルを作成します。

CREATE TABLE char (name char (1), id serial);
INSERT INTO char (name) VALUES ('a');
INSERT INTO char (name) VALUES ('b');
INSERT INTO char (name) VALUES ('c');

次に、その値をグループ化するのは簡単です。

SELECT char.name, COUNT(*) 
  FROM char, film 
  WHERE film.name ILIKE '%' || char.name || '%' 
  GROUP BY char.name 
  ORDER BY char.name;

ILIKEについて心配する必要はありません。

テーブルタイトルとしてキーワード「char」を使用することに100%満足しているわけではありませんが、これまでのところ悪い経験はありませんでした。一方、それは自然な名前です。多分それを別の言語に翻訳するなら-ドイツ語の「zeichen」のように、あなたは曖昧さを避けます。

于 2012-05-12T02:23:44.367 に答える