質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.34%
Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

3回答

402閲覧

【Oracle】LEFT JOINにおける結合条件

ika_kani

総合スコア0

Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

0グッド

1クリップ

投稿2024/12/05 08:49

実現したいこと

サイトのアクセスログからURLのみを抽出し、最終的にURLごとの合計値を出力したいです。
集計の元になるViewを作成し、SQLを実行して出力しようと考えています。
困っているのはVIEWの作成です。

アクセスログが格納されているテーブル(ACCESS_LOG)にはアクセス先のコンテンツやファイル名を含んだURLが保持されていますが、コンテンツやファイル名は不要なので"/"で区切り、サイト一覧(SITE_ALL)から一致する値をURL_sinとして持たせたいです。


ACCESS_LOG

IDURLTIME
A123A123/sample/test.doc2024/12/1 12:00
A123A123/sample/test2.pdf2024/12/1 12:05
A123A123/sample/sub/test3.txt2024/12/1 12:10
B456B456/sample/test4.xlsx2024/12/1 12:30
B456B456/sample/sub/list/test5.png2024/12/1 12:45
B456B456/sample/sub/sub/doclib/test6.pdf2024/12/1 12:50

SITE_ALL

NAMEURL
サイトA1https://A123/sample
サイトA2https://A123/sample/sub
サイトB1https://B456/sample
サイトB2https://B456/sample/sub
サイトB3https://B456/sample/sub/sub

作成したいView

IDURLsub1sub2sub3URL_sinTIME
A123https://A123/sample/test.dochttps://A123/samplehttps://A123https://A123https://A123/sample2024/12/1 12:00
A123https://A123/sample/test2.pdfhttps://A123/samplehttps://A123/https://A123https://A123/sample2024/12/1 12:05
A123https://A123/sample/sub/test3.txthttps://A123/samplehttps://A123/sample/subhttps://A123https://A123/sample/sub2024/12/1 12:10
B456https://B456/sample/test4.xlsxhttps://B456/samplehttps://B456https://B456https://B456/sample2024/12/1 12:30
B456https://B456/sample/sub/list/test5.pnghttps://B456/samplehttps://B456/sample/subhttps://B456/sample/sub/listhttps://B456/sample/sub2024/12/1 12:45
B456https://B456/sample/sub/sub/doclib/test6.pdfhttps://B456/samplehttps://B456/sample/subhttps://B456/sample/sub/subhttps://B456/sample/sub/sub2024/12/1 12:50

最終的にSQLで出力したいデータ

URLアクセス数
https://A123/sample2
https://A123/sample/sub1
https://B456/sample1
https://B456/sample/sub1
https://B456/sample/sub/sub1

発生している問題・分からないこと

ACCESS_LOGとSITE_ALLでキーになる項目がないため、ACCESS_LOGの項目に「https://」を結合し、SUBSTRを使って“/”で抜き出し、この文字列(sub1~3)をキーとして使っています。
また、CASEを使ってsub3→2→1の順に比較してSITE_ALLに存在する値をURL_sinに表示します。

現在はLEFT JOINのON句でORを使っているのですが、ORだと必要のないデータまで表示されてしまい、実際の件数よりも多く表示されてしまいます。
CASEのように、sub3と一致するURL→なければsub2と一致するURL→なければsub1と一致するURLというふうに結合したいです。

また、文字列を結合して使っているのでコードの見栄えが悪く、他の記載方法はありますでしょうか?

作成したいView(一例)

IDURLsub1sub2sub3URL_sinTIME
B456https://B456/sample/sub/sub/doclib/test6.pdfhttps://B456/samplehttps://B456/sample/subhttps://B456/sample/sub/subhttps://B456/sample/sub/sub2024/12/1 12:50

現在のView表示(一例)

IDURLsub1sub2sub3URL_sinTIME
B456https://B456/sample/sub/sub/doclib/test6.pdfhttps://B456/samplehttps://B456/sample/subhttps://B456/sample/sub/subhttps://B456/sample2024/12/1 12:50
B456https://B456/sample/sub/sub/doclib/test6.pdfhttps://B456/samplehttps://B456/sample/subhttps://B456/sample/sub/subhttps://B456/sample/sub2024/12/1 12:50
B456https://B456/sample/sub/sub/doclib/test6.pdfhttps://B456/samplehttps://B456/sample/subhttps://B456/sample/sub/subhttps://B456/sample/sub/sub2024/12/1 12:50

↑本当は1行だけでいいのに、3行表示されてしまう

該当のソースコード

SQL

1CREATE OR REPLACE VIEW ACCESS_LOG_V 2AS 3SELECT AL.ID 4 ,AL.URL 5 ,'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,1)-1) AS “sub1” 6 ,'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,2)-1) AS “sub2” 7 ,'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,3)-1) AS “sub3” 8 ,CASE 9 WHEN 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,3)-1) = SA.URL THEN SA.URL 10 WHEN 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,2)-1) = SA.URL THEN SA.URL 11 WHEN 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,1)-1) = SA.URL THEN SA.URL 12 ELSE 'no' 13 END AS “URL_sin“ 14 ,AL.TIME 15FROM ACCESS_LOG AL 16LEFT JOIN SITE_ALL SA 17ON 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,3)-1) = SA.URL 18OR 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,2)-1) = SA.URL 19OR 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,1)-1) = SA.URL

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

結合条件について調べましたが、ORとAND以外の使い方が見つけられませんでした。
ANDではURL_sinが“no”になってしまうので、今はORを使っています。

補足

ACCESS_LOGのデータがかなり多く、結合した際の負荷が少なくなるように作成したいです。
また、SITE_ALLにACCESS_LOGと共通するIDを追加することは可能です。自分で作成した際はIDの紐付けはうまくいかず追加していない状況です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

「最終的にSQLで出力したいデータ」を実現したいのであれば、ビューをつくらずとも、以下のような再帰SQLを活用したクエリにて実現可能です。

SQL

1WITH A (id, url, time, lvl, url_sin) AS ( 2 SELECT id, url, time, 0, NULL FROM access_log 3 UNION ALL 4 SELECT A.id, A.url, A.time, A.lvl + 1, SA.url 5 FROM A 6 LEFT OUTER JOIN site_all SA ON ('https://' || SUBSTR(A.url, 1, INSTR(A.url, '/', -1, A.lvl + 1) - 1)) = SA.url 7 WHERE INSTR(A.url, '/', -1, A.lvl + 1) > 0 AND url_sin IS NULL 8) 9SELECT url_sin, COUNT(*) 10FROM A 11WHERE url_sin IS NOT NULL 12GROUP BY url_sin

ただデータ量を考慮する必要がある、要件がやや複雑であるなどを考えると、SQLで実行するよりも、バッチ処理などで処理したほうが簡単かつ保守性も高そうな気はします。

なお稼働確認はOralce Database 19cにて行いました。データセットは以下の通りです。

SQL

1CREATE TABLE access_log (id NVARCHAR2(4), url NVARCHAR2(100), time DATE); 2INSERT INTO access_log VALUES ('A123','A123/sample/test.doc',TO_DATE('2024/12/1 12:00', 'YYYY/MM/DD HH:MI')); 3INSERT INTO access_log VALUES ('A123','A123/sample/test2.pdf',TO_DATE('2024/12/1 12:05', 'YYYY/MM/DD HH:MI')); 4INSERT INTO access_log VALUES ('A123','A123/sample/sub/test3.txt',TO_DATE('2024/12/1 12:10', 'YYYY/MM/DD HH:MI')); 5INSERT INTO access_log VALUES ('B456','B456/sample/test4.xlsx',TO_DATE('2024/12/1 12:30', 'YYYY/MM/DD HH:MI')); 6INSERT INTO access_log VALUES ('B456','B456/sample/sub/list/test5.png',TO_DATE('2024/12/1 12:45', 'YYYY/MM/DD HH:MI')); 7INSERT INTO access_log VALUES ('B456','B456/sample/sub/sub/doclib/test6.pdf',TO_DATE('2024/12/1 12:50', 'YYYY/MM/DD HH:MI')); 8 9 10CREATE TABLE site_all (name NVARCHAR2(100), url NVARCHAR2(100)); 11INSERT INTO site_all VALUES('サイトA1','https://A123/sample'); 12INSERT INTO site_all VALUES('サイトA2','https://A123/sample/sub'); 13INSERT INTO site_all VALUES('サイトB1','https://B456/sample'); 14INSERT INTO site_all VALUES('サイトB2','https://B456/sample/sub'); 15INSERT INTO site_all VALUES('サイトB3','https://B456/sample/sub/sub');

投稿2024/12/06 11:32

neko_the_shadow

総合スコア2351

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sazi

2024/12/07 11:25 編集

> SQLで実行するよりも、バッチ処理などで処理したほうが簡単かつ保守性も高そうな気はします。 利用頻度が高いなら、log書込み時にカウントもする方が、コスト的には最も効率的でしょうね
ika_kani

2024/12/09 10:20

みなさま、ご回答いただきありがとうございます。また、回答いただいていたのになかなかお返事できず申し訳ありません。 バッチ処理で出力したことが無かったので、ぜひ経験としても試してみたいと思っております。 こちらの理解が追いついておらず申し訳ないのですが、バッチで処理する場合csvで出力されるのでしょうか?
neko_the_shadow

2024/12/09 13:12

> SQLで実行するよりも、バッチ処理などで処理したほうが簡単かつ保守性も高そうな気はします ここでの意図は「複雑なSQLを作るより、何らかのプログラミング言語で処理したり、csvにエクスポートした後にExcelで集計したりしたほうが、いろいろとメリットが大きい可能性がある」ということでした。わたしの書き方が誤解を招く書き方になっていました。申し訳ありません。 プログラミング言語からOracle Databaseに接続する方法やcsvにエクスポートする方法はWeb上にいくらでもサンプルがあると思いますので、調べていただければよいかと思います。
guest

0

サイトのURLの階層関係を浅い順に「A,B,C」という集合で表すと
「A > B > C」となってAにはBもCも含まれています。
それをORで抽出すると結果的にA全体を求める事になります。
やりたいことは、
・Bを除いたAの件数
・Cを除いたBの件数
・Cのみの件数
ですので、それぞれで件数を求めてUNION等を用いて一つの表にすれば良いと思います。

投稿2024/12/06 07:01

sazi

総合スコア25331

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ika_kani

2024/12/12 09:42

お世話になっております。 Unionを使った方法も試しているところなのですが、「Bを除いたAの件数」という条件式がなかなかできません。実現方法あればご教示いただきたく、よろしくお願いします。
guest

0

パフォーマンスはさておき、
DISTINCT句でどうでしょうか?

sql

1CREATE OR REPLACE VIEW ACCESS_LOG_V 2AS 3SELECT DISTINCT AL.ID --「DISTINCT」をSELECT句の次に入れる 4 ,AL.URL 5 ,'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,1)-1) AS “sub1” 6 ,'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,2)-1) AS “sub2” 7 ,'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,3)-1) AS “sub3” 8 ,CASE 9 WHEN 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,3)-1) = SA.URL THEN SA.URL 10 WHEN 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,2)-1) = SA.URL THEN SA.URL 11 WHEN 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,1)-1) = SA.URL THEN SA.URL 12 ELSE 'no' 13 END AS “URL_sin“ 14 ,AL.TIME 15FROM ACCESS_LOG AL 16LEFT JOIN SITE_ALL SA 17ON 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,3)-1) = SA.URL 18OR 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,2)-1) = SA.URL 19OR 'https://' || SUBSTR(AL.URL,1,INSTR(AL.URL,'/',1,1)-1) = SA.URL

投稿2024/12/06 04:10

odataiki

総合スコア973

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.34%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問