質問
私は2つの列を持つテーブルを持っています、FirstNameとLastNameとしましょう。最初のテーブルからのFirstNameのすべてのペアに共通のLastNameの数が含まれている別のテーブルを取得する必要があります。
これはSQLで行うことも可能ですか?
これがクエリの効率に影響を与える場合、FirstNameよりもはるかに多くの一意のLastNameがあります。
おもちゃの例、入力:
FirstName, LastName
John, Smith
John, Doe
Jane, Doe
出力:
FirstName1, FirstName2, CommonLastNames
John, John, 2
John, Jane, 1
Jane, Jane, 1
Jane, John, 1
この関係は反射的で対称的であるため、結果が三角形の1つ(たとえば、対角線の上のもの)であれば問題ありません。
解決
手にコピーを持っているので、MS SQL Serverを実行するつもりです。私はほとんどの専攻が同様にそれをするだろうと私は信じています。
サンプルテーブルとデータ付きサンプルテーブル。テーブル変数を使用しますが、テーブルのフレーバーについても同じです。
declare @t table (FirstName char(10), LastName char(10));
insert @t(FirstName,LastName)
values ('John','Smith'),('John','Doe'),('Jane','Doe');
.
自己結合をすることですべてのペアを入手できます。
select
a.FirstName, a.LastName, b.FirstName, b.LastName
from @t as a
cross apply @t as b;
.
CROSS APPLY
を使用して、HOOPSを通過してON
句の結合条件を見つける必要があります。
次に数える必要があります。これがCASE
ステートメントが入ってきた場所です。ケースは、最初の名前のペアごとに整数値を返します。これはカウントされるものです。(最後の質問が一致するようにあなたの質問を正しく読んでいるなら、それが私が持っている比較です。うまくいけば、私が間違っているならばこれを修正する方法は明らかです。)
select
...
case
when a.LastName = b.LastName then 1
else 0
end
...etc.
.
SUM()
とGROUP BY
で追加し、あなたはあなたの答えを得る:
select
a.FirstName,
b.FirstName,
sum(
case
when a.LastName = b.LastName then 1
else 0
end
) as CommonLastNames
from @t as a
cross apply @t as b
group by a.FirstName, b.FirstName;
. 他のヒント
私は私の質問を認めなければなりませんでした少し欠陥がありました。 私が本当に必要なのは、最初の最初の名前からの最初のFirstNameのペアでは、「共通のLastNameの数」を含んでいませんでした。実際、私はゼロカウントでペアを気にしません。
質問が修正されると、解ははるかに速くなります。
入力を与えた:
create local temp table t (FirstName char(10), LastName char(10)) ON COMMIT PRESERVE ROWS;
insert into t(FirstName,LastName) values ('John','Smith');
insert into t(FirstName,LastName) values ('John','Doe');
insert into t(FirstName,LastName) values ('Jane','Doe');
.
元の質問の場合、解決策はO(n ^ 2)です(質問が「すべてのペア」を主張するため):
select a.FirstName, b.FirstName,
sum(case when a.LastName = b.LastName then 1 else 0 end) CommonNames
from t a, t b group by 1, 2;
.
ゼロカウントをスキップするのが問題ない場合、LastNameの自己結合は、はるかに速く機能します(データが十分に疎になると仮定):
select a.FirstName, b.FirstName,
count(*) CommonNames from t a
join t b using (LastName) group by 1, 2;
.
私はまだこの些細な解決策を逃したのだろうか。
DOH!これが良い方法です:
SELECT city_a, city_b, COUNT(*)
FROM (
SELECT a.city city_a,
a.state,
b.city city_b
FROM us a
CROSS JOIN us b
WHERE a.state = b.state
AND a.city < b.city
) x
GROUP BY city_a, city_b
ORDER BY 3 DESC;
.
出力:
+-----------+-------------+----------+
| city_a | city_b | COUNT(*) |
+-----------+-------------+----------+
| Lebanon | Springfield | 5 |
| Bedford | Franklin | 4 | -- as shown in previous 'answer'
| Franklin | Lebanon | 4 |
| Franklin | Hudson | 4 |
| Franklin | Salem | 4 |
| Hudson | Salem | 4 |
| Salem | Springfield | 4 |
| Clinton | Columbia | 4 |
| Auburn | Fairfield | 3 |
| Auburn | Madison | 3 |
...
(2.63 sec) -- for all 4175 cities in `us`.
.
Synity On First Item:
mysql> SELECT city, state FROM us WHERE city IN ('Lebanon', 'Springfield');
+-------------+-------+
| city | state |
+-------------+-------+
| Springfield | FL |
| Springfield | IL |
| Lebanon | IN |
| Springfield | MA |
| Lebanon | ME |
| Lebanon | MO |
| Springfield | MO |
| Lebanon | NH |
| Springfield | NJ |
| Lebanon | OH |
| Springfield | OH |
| Lebanon | OR |
| Springfield | OR |
| Lebanon | PA |
| Springfield | PA |
| Lebanon | TN |
| Springfield | TN |
| Springfield | VA |
| Springfield | VT |
+-------------+-------+
19 rows in set (0.00 sec)
.
メインハンドラ%ステータス値は、それが多くの作業をしたが、かなりo(n * n)ではないことを示しています(おそらくクロスジョインは一度に1つの状態だけなので)
| Handler_read_key | 4176 |
| Handler_read_next | 667294 |
| Handler_read_rnd | 1742 |
| Handler_read_rnd_next | 701964 |
| Handler_update | 1731 |
| Handler_write | 703693 |
.
百万の列への外挿 - それはおそらく数日かかるでしょう。
それは興味深い挑戦でした。米国の都市のリストを使用して、私はこの解決策を思いつきました(MySQLで):
SELECT city_a, city_b,
COUNT(DISTINCT state)
FROM (
( SELECT a.city city_a,
b.city city_b,
a.state -- This line differs
FROM us a
CROSS JOIN us b
WHERE a.state = b.state
AND a.city != b.city -- Added (to avoid noise)
AND a.city < 'M' -- to speed up test
AND b.city < 'M'
)
UNION ALL
( SELECT a.city city_a,
b.city city_b,
b.state -- This line differs
FROM us a
CROSS JOIN us b
WHERE a.state = b.state
AND a.city != b.city -- Added (to avoid noise)
AND a.city < 'M' -- to speed up test
AND b.city < 'M'
)
) ab
GROUP BY 1, 2
HAVING COUNT(DISTINCT state) > 1
ORDER BY COUNT(DISTINCT state) desc
INDEX(state, city)
パフォーマンスに役立ちます。
その結果は以下の通りです:
+----------+------------+-----------------------+
| city_a | city_b | COUNT(DISTINCT state) |
+----------+------------+-----------------------+
| Franklin | Bedford | 4 |
| Lebanon | Franklin | 4 |
| Franklin | Lebanon | 4 |
| Hudson | Franklin | 4 |
| Columbia | Clinton | 4 |
| Clinton | Columbia | 4 |
| Franklin | Hudson | 4 |
| Bedford | Franklin | 4 |
| Lebanon | Farmington | 3 |
| Hanover | Kingston | 3 |
...
(25.17 sec)
アルファベット全体を含めるのに4倍の時間がかかったかもしれません。テーブルには4K行しかなかったので、これは次のとおりです ない 速い仕事。
結果の「証明」:mysql>SELECT city,state FROM us where city IN('Franklin','Bedford');
+----------+-------+
| city | state |
+----------+-------+
| Bedford | IN |
| Franklin | IN |
| Bedford | MA |
| Franklin | MA |
| Bedford | NH |
| Franklin | NH |
| Bedford | OH |
| Franklin | OH |
| Franklin | TN |
| Bedford | TX |
| Franklin | WI |
+----------+-------+
11 rows in set (0.00 sec)