ソースコード

8章 データベースの操作2

8.6節から8.8節

8.6 グラフの操作

8.6.1 テーブルの準備

PostgreSQL
CREATE LANGUAGE plpgsql;
Oracle
SET AUTOCOMMIT ON;
SET TIMING ON;
SET SERVEROUTPUT ON;
DB2
データベース・ロギングの構成ウィザードで、各ログ・ファイルのサイズを1024から4096に変更

テーブル:nodes

MySQL, PostgreSQL, DB2, SQL Server
CREATE TABLE nodes(
  id INT NOT NULL,
  name VARCHAR(20) DEFAULT '' NOT NULL,
  CONSTRAINT nodes_pri PRIMARY KEY (id)
);
CREATE INDEX name_idx ON nodes (name);
Oracle
CREATE TABLE nodes (
  id INT NOT NULL,
  name VARCHAR (20),
  CONSTRAINT nodes_pri PRIMARY KEY (id)
);
CREATE INDEX name_idx ON nodes (name);

テーブル:edges

共通
CREATE TABLE edges(
  head INT NOT NULL,
  tail INT NOT NULL,
  CONSTRAINT edges_pri PRIMARY KEY (head,tail)
);

P.100でインデックスの効果を見る際に、以下を実行する。

CREATE INDEX head_idx ON edges (head);
CREATE INDEX tail_idx ON edges (tail);

サンプルデータ

MySQL, PostgreSQL, DB2
INSERT INTO nodes (id,name) VALUES
(1,'A1'),(2,'A2'),(3,'A3'),(4,'A4'),(5,'A5'),
(6,'A6'),(7,'A7'),(8,'A8'),(9,'A9');

INSERT INTO edges (head,tail) VALUES
(1,2),(1,3),(1,4),(2,5),(2,6),
(3,6),(3,7),(6,8),(6,9);
Oracle, SQL Server
INSERT INTO nodes (id,name) VALUES (1,'A1');
INSERT INTO nodes (id,name) VALUES (2,'A2');
INSERT INTO nodes (id,name) VALUES (3,'A3');
INSERT INTO nodes (id,name) VALUES (4,'A4');
INSERT INTO nodes (id,name) VALUES (5,'A5');
INSERT INTO nodes (id,name) VALUES (6,'A6');
INSERT INTO nodes (id,name) VALUES (7,'A7');
INSERT INTO nodes (id,name) VALUES (8,'A8');
INSERT INTO nodes (id,name) VALUES (9,'A9');

INSERT INTO edges (head,tail) VALUES (1,2);
INSERT INTO edges (head,tail) VALUES (1,3);
INSERT INTO edges (head,tail) VALUES (1,4);
INSERT INTO edges (head,tail) VALUES (2,5);
INSERT INTO edges (head,tail) VALUES (2,6);
INSERT INTO edges (head,tail) VALUES (3,6);
INSERT INTO edges (head,tail) VALUES (3,7);
INSERT INTO edges (head,tail) VALUES (6,8);
INSERT INTO edges (head,tail) VALUES (6,9);

ノードあたりの平均エッジ数

共通
SELECT AVG(c) FROM (
  SELECT id,SUM(c) AS c FROM (
    (SELECT head AS id,COUNT(tail) AS c FROM edges GROUP BY head)
    UNION ALL
    (SELECT tail AS id,COUNT(head) AS c FROM edges GROUP by tail)
  ) tmp
  GROUP BY id
) tmp2;

大きなグラフの作成(下のmakeNumSeqを作ってから)

MySQL
CALL makeNumSeq(500);
TRUNCATE TABLE nodes;

INSERT INTO nodes (id,name) SELECT n AS id,'' AS name FROM numSequence;

TRUNCATE TABLE edges;

INSERT INTO edges (head,tail)
  SELECT a.id AS head,b.id AS tail
  FROM nodes a,nodes b
  WHERE a.id!=b.id
  ORDER BY RAND()
  LIMIT 1250; -- 5*500/2
PostgreSQL
SELECT makeNumSeq(500);
TRUNCATE TABLE nodes;

INSERT INTO nodes (id,name) SELECT n AS id,'' AS name FROM numSequence;

TRUNCATE TABLE edges;

INSERT INTO edges (head,tail)
  SELECT a.id AS head,b.id AS tail
  FROM nodes a,nodes b
  WHERE a.id!=b.id
  ORDER BY RANDOM()
  LIMIT FLOOR(5*500/2);
Oracle
CALL makeNumSeq(500);
TRUNCATE TABLE nodes;

INSERT INTO nodes (id,name) SELECT n AS id,'' AS name FROM numSequence;

TRUNCATE TABLE edges;

INSERT INTO edges (head,tail)
  SELECT * FROM (
    SELECT a.id AS head,b.id AS tail
    FROM nodes a,nodes b
    WHERE a.id!=b.id
    ORDER BY DBMS_RANDOM.VALUE()
  ) tmp
  WHERE ROWNUM<=FLOOR(5*500/2);
DB2
CALL makeNumSeq(500);
DELETE FROM nodes;

INSERT INTO nodes (id,name) SELECT n AS id,'' AS name FROM numSequence;

DELETE FROM edges;

INSERT INTO edges (head,tail)
  SELECT a.id head,b.id tail
  FROM nodes a,nodes b
  WHERE a.id!=b.id
  ORDER BY RAND()
  FETCH FIRST 1250 ROWS ONLY;
-- 5*500/2

(DB2ではステートメント終了文字を「@」にしてください。)

SQL Server
EXECUTE makeNumSeq 500
GO
TRUNCATE TABLE nodes
GO

INSERT INTO nodes (id,name) SELECT n AS id,'' AS name FROM numSequence;

TRUNCATE TABLE edges
GO

INSERT INTO edges (head,tail)
  SELECT TOP (5*500/2) a.id head,b.id tail
  FROM nodes a,nodes b
  WHERE a.id!=b.id
  ORDER BY NEWID()

8.8 ストアド・プロシジャ

8.8.1 連番データの作成

MySQL
DROP TABLE IF EXISTS numSequence;
CREATE TABLE numSequence (n INT);
PostgreSQL, Oracle, DB2
CREATE TABLE numSequence (n INT);

(DB2ではステートメント終了文字を「@」にしてください。)

SQL Server
IF EXISTS (SELECT * FROM dbo.sysobjects
  WHERE id = OBJECT_ID(N'numSequence') AND
    OBJECTPROPERTY(id, N'IsUserTable') = 1)
  DROP TABLE numSequence
GO

CREATE TABLE numSequence (n INT)
GO

プロシジャ:makeNumSeq

(DB2ではステートメント終了文字を「@」にしてください。)

別の方法
PostgreSQL
TRUNCATE TABLE nodes;

INSERT INTO nodes (id,name)
  SELECT id,'' AS name
  FROM GENERATE_SERIES (1,500,1) x (id);
Oracle
TRUNCATE TABLE nodes;

INSERT INTO nodes (id,name)
WITH x
AS (
  SELECT level AS id FROM DUAL
  CONNECT BY level<=500
)
SELECT id,'' AS name FROM x;
DB2
DELETE FROM nodes;

INSERT INTO nodes (id,name)
WITH x (id) AS (
  SELECT 1 FROM SYSIBM.SYSDUMMY1
  UNION ALL
  SELECT id+1 FROM x WHERE id<500)
SELECT id,'' AS name FROM x;

(DB2ではステートメント終了文字を「@」にしてください。)

8.8.2 最短経路長

テーブル:paths

共通
CREATE TABLE paths(
  startNode INT NOT NULL,
  endNode INT NOT NULL,
  length INT NOT NULL,
  CONSTRAINT paths_pri PRIMARY KEY (startNode,endNode)
);
CREATE INDEX s_idx ON paths (startNode);
CREATE INDEX e_idx ON paths (endNode);
CREATE INDEX l_idx ON paths (length);

プロシジャ:shortestPath

(DB2ではステートメント終了文字を「@」にしてください。)

結果の確認

共通
SELECT MAX(length) FROM paths;

グラフが大きいときは、次のSQL文を実行しないでください。

共通
SELECT * FROM paths ORDER BY startNode,endNode;