メモ:xUnit.net でテストを並列で動かすのを辞める (not run in parallel)

DB を使うテストでDBの初期化と他のテストでのDBアクセスがバッティングして初期化に失敗してハマったのでメモ。

デフォルトだと、テストクラス毎に並列 (in Parallel) で動く。
Running Tests in Parallel > xUnit.net
↑の中身をざっくりと書くと、

  • Test Collection 単位で並列にテストを実行。
    • 同じ Test Collection 内のテストは直列 (not run in parallel) になる。
  • デフォルトでは、1テストクラス = 1 Test Collection
  • アセンブリに CollectionBehavior を付けることで、Test Collection の単位や並列の動作を変更出来る
  • テストクラス に CollectionAttribute を付けることで、自由に Test Collection を指定出来る。
    • name が同じなら同じ Test Collection

SQL Server のグラフでレコメンドをやってみる

SQL Server のグラフを色々試していくシリーズ。

3回目はレコメンド検索です。
この商品を買った人はこんな商品も買っていますっていうよくあるやつです。

今回はイベントのセッションを対象にして、このセッションを受けた人はこんなセッションも受けていますっていうので試します。

いつものように比較用に通常のテーブルでも試します。
可視化は Neo4j ですが、見難いですね。
f:id:odashinsuke:20180108125814p:plain

ノードが、セッション、参加者でエッジがセッション参加者です。

drop table G_セッション
drop table G_参加者
drop table G_セッション参加者

create table G_セッション (
  Id int not null primary key,
  セッション名 nvarchar(20) not null,
  ルーム char(1) not null,
  コマ int not null 
) as Node
create table G_参加者 (
  Id int not null primary key,
  名前 nvarchar(20) not null
) as Node
create table G_セッション参加者 as Edge

insert into G_セッション values
(1, N'セッションA-1', 'A', 1),
(2, N'セッションA-2', 'A', 2),
(3, N'セッションA-3', 'A', 3),
(4, N'セッションA-4', 'A', 4),
(5, N'セッションB-1', 'B', 1),
(6, N'セッションB-2', 'B', 2),
(7, N'セッションB-3', 'B', 3),
(8, N'セッションB-4', 'B', 4)

insert into G_参加者 values
(1, N'参加者1'),
(2, N'参加者2'),
(3, N'参加者3'),
(4, N'参加者4'),
(5, N'参加者5'),
(6, N'参加者6'),
(7, N'参加者7'),
(8, N'参加者8'),
(9, N'参加者9'),
(10, N'参加者10'),
(11, N'参加者11'),
(12, N'参加者12'),
(13, N'参加者13'),
(14, N'参加者14'),
(15, N'参加者15'),
(16, N'参加者16'),
(17, N'参加者17'),
(18, N'参加者18'),
(19, N'参加者19'),
(20, N'参加者20')

declare @s1 nvarchar(max) = (select $node_id from G_セッション where Id = 1)
declare @s2 nvarchar(max) = (select $node_id from G_セッション where Id = 2)
declare @s3 nvarchar(max) = (select $node_id from G_セッション where Id = 3)
declare @s4 nvarchar(max) = (select $node_id from G_セッション where Id = 4)
declare @s5 nvarchar(max) = (select $node_id from G_セッション where Id = 5)
declare @s6 nvarchar(max) = (select $node_id from G_セッション where Id = 6)
declare @s7 nvarchar(max) = (select $node_id from G_セッション where Id = 7)
declare @s8 nvarchar(max) = (select $node_id from G_セッション where Id = 8)

declare @u1 nvarchar(max) = (select $node_id from G_参加者 where Id = 1)
declare @u2 nvarchar(max) = (select $node_id from G_参加者 where Id = 2)
declare @u3 nvarchar(max) = (select $node_id from G_参加者 where Id = 3)
declare @u4 nvarchar(max) = (select $node_id from G_参加者 where Id = 4)
declare @u5 nvarchar(max) = (select $node_id from G_参加者 where Id = 5)
declare @u6 nvarchar(max) = (select $node_id from G_参加者 where Id = 6)
declare @u7 nvarchar(max) = (select $node_id from G_参加者 where Id = 7)
declare @u8 nvarchar(max) = (select $node_id from G_参加者 where Id = 8)
declare @u9 nvarchar(max) = (select $node_id from G_参加者 where Id = 9)
declare @u10 nvarchar(max) = (select $node_id from G_参加者 where Id = 10)
declare @u11 nvarchar(max) = (select $node_id from G_参加者 where Id = 11)
declare @u12 nvarchar(max) = (select $node_id from G_参加者 where Id = 12)
declare @u13 nvarchar(max) = (select $node_id from G_参加者 where Id = 13)
declare @u14 nvarchar(max) = (select $node_id from G_参加者 where Id = 14)
declare @u15 nvarchar(max) = (select $node_id from G_参加者 where Id = 15)
declare @u16 nvarchar(max) = (select $node_id from G_参加者 where Id = 16)
declare @u17 nvarchar(max) = (select $node_id from G_参加者 where Id = 17)
declare @u18 nvarchar(max) = (select $node_id from G_参加者 where Id = 18)
declare @u19 nvarchar(max) = (select $node_id from G_参加者 where Id = 19)
declare @u20 nvarchar(max) = (select $node_id from G_参加者 where Id = 20)

insert into G_セッション参加者 values
(@u1, @s1),
(@u5, @s1),
(@u10, @s1),
(@u11, @s1),
(@u2, @s5),
(@u3, @s5),
(@u4, @s5),
(@u6, @s5),
(@u7, @s5),
(@u8, @s5),
(@u9, @s5),
(@u12, @s5),
(@u13, @s5),
(@u15, @s5),
(@u17, @s5),
(@u20, @s5),
(@u1, @s2),
(@u2, @s2),
(@u3, @s2),
(@u5, @s2),
(@u7, @s2),
(@u9, @s2),
(@u10, @s2),
(@u11, @s2),
(@u13, @s2),
(@u16, @s2),
(@u18, @s2),
(@u4, @s6),
(@u6, @s6),
(@u8, @s6),
(@u12, @s6),
(@u15, @s6),
(@u17, @s6),
(@u19, @s6),
(@u1, @s3),
(@u3, @s3),
(@u6, @s3),
(@u7, @s3),
(@u9, @s3),
(@u10, @s3),
(@u12, @s3),
(@u14, @s3),
(@u2, @s7),
(@u4, @s7),
(@u8, @s7),
(@u11, @s7),
(@u15, @s7),
(@u16, @s7),
(@u17, @s7),
(@u18, @s7),
(@u19, @s7),
(@u20, @s7),
(@u1, @s4),
(@u2, @s4),
(@u3, @s4),
(@u4, @s4),
(@u5, @s4),
(@u6, @s4),
(@u7, @s4),
(@u8, @s4),
(@u9, @s4),
(@u10, @s4),
(@u11, @s8),
(@u12, @s8),
(@u13, @s8),
(@u14, @s8),
(@u15, @s8),
(@u16, @s8),
(@u17, @s8),
(@u18, @s8),
(@u19, @s8),
(@u20, @s8)

ついでに通常のテーブルの方も作ってしまいましょう。

drop table セッション
drop table 参加者
drop table セッション参加者

create table セッション (
  Id int not null primary key, 
  セッション名 nvarchar(20) not null, 
  ルーム char(1) not null,
  コマ int not null 
)
create table 参加者 (
  Id int not null primary key, 
  名前 nvarchar(20) not null
)
create table セッション参加者 (
  参加者Id int not null, 
  セッションId int not null, 
  primary key (参加者Id, セッションId)
)

insert into セッション values
(1, N'セッションA-1', 'A', 1), 
(2, N'セッションA-2', 'A', 2), 
(3, N'セッションA-3', 'A', 3), 
(4, N'セッションA-4', 'A', 4), 
(5, N'セッションB-1', 'B', 1), 
(6, N'セッションB-2', 'B', 2), 
(7, N'セッションB-3', 'B', 3), 
(8, N'セッションB-4', 'B', 4)

insert into 参加者 values
(1, N'参加者1'), 
(2, N'参加者2'), 
(3, N'参加者3'), 
(4, N'参加者4'), 
(5, N'参加者5'), 
(6, N'参加者6'), 
(7, N'参加者7'), 
(8, N'参加者8'), 
(9, N'参加者9'), 
(10, N'参加者10'), 
(11, N'参加者11'), 
(12, N'参加者12'), 
(13, N'参加者13'), 
(14, N'参加者14'), 
(15, N'参加者15'), 
(16, N'参加者16'), 
(17, N'参加者17'), 
(18, N'参加者18'), 
(19, N'参加者19'), 
(20, N'参加者20')

insert into セッション参加者 values
(1, 1), 
(5, 1), 
(10, 1), 
(11, 1), 
(2, 5), 
(3, 5), 
(4, 5), 
(6, 5), 
(7, 5), 
(8, 5), 
(9, 5), 
(12, 5), 
(13, 5), 
(15, 5), 
(17, 5), 
(20, 5),
(1, 2), 
(2, 2),
(3, 2),
(5, 2), 
(7, 2),
(9, 2), 
(10, 2), 
(11, 2),
(13, 2),
(16, 2),
(18, 2),
(4, 6), 
(6, 6), 
(8, 6), 
(12, 6),
(15, 6),
(17, 6),
(19, 6),
(1, 3), 
(3, 3), 
(6, 3), 
(7, 3),
(9, 3),
(10, 3),
(12, 3),
(14, 3),
(2, 7),
(4, 7),
(8, 7),
(11, 7),
(15, 7),
(16, 7),
(17, 7),
(18, 7),
(19, 7),
(20, 7),
(1, 4), 
(2, 4),
(3, 4),
(4, 4),
(5, 4),
(6, 4),
(7, 4),
(8, 4),
(9, 4),
(10, 4),
(11, 8),
(12, 8),
(13, 8),
(14, 8),
(15, 8),
(16, 8),
(17, 8),
(18, 8),
(19, 8),
(20, 8)

レコメンドなので普通に繋いでいきます。
お題は、
セッション.Id = 3 を受講している 参加者.Id = 1 以外の参加者が受講している他のセッションを受講者が多い順に並べる
でいきます。

select
  レコメンドセッション.Id
  , レコメンドセッション.セッション名
  , レコメンドセッション.ルーム
  , レコメンドセッション.コマ
  , count(*) as 受講者数
from
  G_参加者 対象参加者
  , G_セッション 対象セッション 
  , G_セッション参加者 対象セッション_E
  , G_参加者 対象セッション参加者_対象参加者除く 
  , G_セッション参加者 他の参加者が受けてるセッション
  , G_セッション レコメンドセッション
where
  対象参加者.Id = 1
  and 対象セッション.Id = 3
  and 対象セッション参加者_対象参加者除く.Id <> 対象参加者.Id
  and レコメンドセッション.Id <> 対象セッション.Id
  and match (対象セッション <- (対象セッション_E) - 対象セッション参加者_対象参加者除く 
             - (他の参加者が受けてるセッション) -> レコメンドセッション)
group by
  レコメンドセッション.Id
  , レコメンドセッション.セッション名
  , レコメンドセッション.ルーム
  , レコメンドセッション.コマ
order by 
  受講者数 desc

f:id:odashinsuke:20180108130217p:plain
そのままな感じの where の内容ですね。
match を使うことで繋がりが見やすくなってるのかな?

通常のテーブルはこちら。

select
  レコメンドセッション.Id
  , レコメンドセッション.セッション名
  , レコメンドセッション.ルーム
  , レコメンドセッション.コマ
  , count(*) as 受講者数
from
  参加者 対象参加者
  , セッション 対象セッション
  , セッション参加者 対象セッション参加者_対象参加者除く
  , セッション参加者 他の参加者が受けてるセッション
  , セッション レコメンドセッション
where
  対象参加者.Id = 1
  and 対象セッション.Id = 3
  and 対象セッション.Id = 対象セッション参加者_対象参加者除く.セッションId 
  and 対象参加者.Id <> 対象セッション参加者_対象参加者除く.参加者Id
  and 対象セッション参加者_対象参加者除く.参加者Id = 他の参加者が受けてるセッション.参加者Id
  and 他の参加者が受けてるセッション.セッションId <> 対象セッション.Id
  and 他の参加者が受けてるセッション.セッションId = レコメンドセッション.Id
group by
  レコメンドセッション.Id
  , レコメンドセッション.セッション名
  , レコメンドセッション.ルーム
  , レコメンドセッション.コマ
order by 
  受講者数 desc
and 対象セッション参加者_対象参加者除く.参加者Id = 他の参加者が受けてるセッション.参加者Id
and 他の参加者が受けてるセッション.セッションId = レコメンドセッション.Id

が match

match (対象セッション <- (対象セッション_E) - 対象セッション参加者_対象参加者除く - (他の参加者が受けてるセッション) -> レコメンドセッション)

の部分の代替ですが、そんな変わらないかな?

レコメンドの例は、Ignite
Graph extensions in Microsoft SQL Server 2017 and Azure SQL Database
でも紹介されていましたが、こちらは通常のテーブルを使ったクエリの書き方が「非常に悪い」ためグラフテーブルのクエリが見やすく感じる内容になってます。

ですが、実際に書いた感じだとそんな変わらないかな?
SQL Server のグラフで最短経路と取ってみる - お だ のスペース
SQL Server のグラフで巡回セールスマン問題をやってみる - お だ のスペース
前回、前々回と色々試してみましたが、クエリ書いた感じは通常のテーブルとそんな変わらないですが、パフォーマンスはどうなんでしょうねー?
グラフについては、今後の機能追加でクエリが書きやすくなるそうなので、そこに期待ですね。

今回も Neo4j の cypher 貼っときますが、そんな違いないかな?

create (s1:Session{Id:1, Name:"セッションA-1", Room:"A", Schedule:1}), 
  (s2:Session{Id:2, Name:"セッションA-2", Room:"A", Schedule:2}),
  (s3:Session{Id:3, Name:"セッションA-3", Room:"A", Schedule:3}), 
  (s4:Session{Id:4, Name:"セッションA-4", Room:"A", Schedule:4}), 
  (s5:Session{Id:5, Name:"セッションB-1", Room:"B", Schedule:1}), 
  (s6:Session{Id:6, Name:"セッションB-2", Room:"B", Schedule:2}),
  (s7:Session{Id:7, Name:"セッションB-3", Room:"B", Schedule:3}), 
  (s8:Session{Id:8, Name:"セッションB-4", Room:"B", Schedule:4}),  
  (u1:Attendee{Id:1, Name:"参加者1"}),
  (u2:Attendee{Id:2, Name:"参加者2"}),
  (u3:Attendee{Id:3, Name:"参加者3"}),
  (u4:Attendee{Id:4, Name:"参加者4"}),
  (u5:Attendee{Id:5, Name:"参加者5"}),
  (u6:Attendee{Id:6, Name:"参加者6"}),
  (u7:Attendee{Id:7, Name:"参加者7"}),
  (u8:Attendee{Id:8, Name:"参加者8"}),
  (u9:Attendee{Id:9, Name:"参加者9"}),
  (u10:Attendee{Id:10, Name:"参加者10"}),
  (u11:Attendee{Id:11, Name:"参加者11"}),
  (u12:Attendee{Id:12, Name:"参加者12"}),
  (u13:Attendee{Id:13, Name:"参加者13"}),
  (u14:Attendee{Id:14, Name:"参加者14"}),
  (u15:Attendee{Id:15, Name:"参加者15"}),
  (u16:Attendee{Id:16, Name:"参加者16"}),
  (u17:Attendee{Id:17, Name:"参加者17"}),
  (u18:Attendee{Id:18, Name:"参加者18"}),
  (u19:Attendee{Id:19, Name:"参加者19"}),
  (u20:Attendee{Id:20, Name:"参加者20"}),
  (u1)-[:Attend]->(s1),
  (u5)-[:Attend]->(s1),
  (u10)-[:Attend]->(s1),
  (u11)-[:Attend]->(s1),
  (u2)-[:Attend]->(s5),
  (u3)-[:Attend]->(s5),
  (u4)-[:Attend]->(s5),
  (u6)-[:Attend]->(s5),
  (u7)-[:Attend]->(s5),
  (u8)-[:Attend]->(s5),
  (u9)-[:Attend]->(s5),
  (u12)-[:Attend]->(s5),
  (u13)-[:Attend]->(s5),
  (u15)-[:Attend]->(s5),
  (u17)-[:Attend]->(s5),
  (u20)-[:Attend]->(s5),
  (u1)-[:Attend]->(s2),
  (u2)-[:Attend]->(s2),
  (u3)-[:Attend]->(s2),
  (u5)-[:Attend]->(s2),
  (u7)-[:Attend]->(s2),
  (u9)-[:Attend]->(s2),
  (u10)-[:Attend]->(s2),
  (u11)-[:Attend]->(s2),
  (u13)-[:Attend]->(s2),
  (u16)-[:Attend]->(s2),
  (u18)-[:Attend]->(s2),
  (u4)-[:Attend]->(s6),
  (u6)-[:Attend]->(s6),
  (u8)-[:Attend]->(s6),
  (u12)-[:Attend]->(s6),
  (u15)-[:Attend]->(s6),
  (u17)-[:Attend]->(s6),
  (u19)-[:Attend]->(s6),
  (u1)-[:Attend]->(s3),
  (u3)-[:Attend]->(s3),
  (u6)-[:Attend]->(s3),
  (u7)-[:Attend]->(s3),
  (u9)-[:Attend]->(s3),
  (u10)-[:Attend]->(s3),
  (u12)-[:Attend]->(s3),
  (u14)-[:Attend]->(s3),
  (u2)-[:Attend]->(s7),
  (u4)-[:Attend]->(s7),
  (u8)-[:Attend]->(s7),
  (u11)-[:Attend]->(s7),
  (u15)-[:Attend]->(s7),
  (u16)-[:Attend]->(s7),
  (u17)-[:Attend]->(s7),
  (u18)-[:Attend]->(s7),
  (u19)-[:Attend]->(s7),
  (u20)-[:Attend]->(s7),
  (u1)-[:Attend]->(s4),
  (u2)-[:Attend]->(s4),
  (u3)-[:Attend]->(s4),
  (u4)-[:Attend]->(s4),
  (u5)-[:Attend]->(s4),
  (u6)-[:Attend]->(s4),
  (u7)-[:Attend]->(s4),
  (u8)-[:Attend]->(s4),
  (u9)-[:Attend]->(s4),
  (u10)-[:Attend]->(s4),
  (u11)-[:Attend]->(s8),
  (u12)-[:Attend]->(s8),
  (u13)-[:Attend]->(s8),
  (u14)-[:Attend]->(s8),
  (u15)-[:Attend]->(s8),
  (u16)-[:Attend]->(s8),
  (u17)-[:Attend]->(s8),
  (u18)-[:Attend]->(s8),
  (u19)-[:Attend]->(s8),
  (u20)-[:Attend]->(s8)
match (s:Session {Id:3})<-[:Attend]-(a:Attendee)-[:Attend]->(r:Session) 
where a.Id <> 1 
return r, count(*) as cnt 
order by cnt desc

01/23(火) SQLWorld★大阪#45 開催します

SqlWorld :: SQLWorld★大阪#45 開催します。27回目の平日夜開催で、前回同様 ハンズオン 形式行う予定です。

【日時】
2018年01月23日(火曜日) 19:00~21:00
 

【イベント概要】
SQLWorld 27回目の平日夜開催~。今回も、みんなで SQL を書いてみようというハンズオン企画です!ブラウザがあれば参加出来るようにしていますので、iPad 等のタブレットでも大丈夫です。
 

【会場】
フェンリル株式会社さま大阪本社 http://www.fenrir-inc.com/
〒530-0011 大阪府大阪市北区大深町 3番1号 グランフロント大阪タワーB(オフィス)
 

【参加費】
無料
 

【持ち物】
パソコン/タブレット (DB のインストールは不要です。)
 

【参加可能人数】
13 人
 

お題に沿って、SQL を書いてみようという勉強会です。是非ご参加を~。

開催回数は増えていっていますが、続き物というわけでは無いので初めて参加される方でもお気軽にどぞー。

.NET Core で TransactionScope を使ってみる (Preview)

だいぶ前にこれ書いて放置してたんですが、
.NET Core で SqlClient と TransactionScope は 2.1 から? - お だ のスペース

ムッシュが何か書いてたので久々に検証してみました。
SQL Server 2017 の on Linux における分散トランザクションのサポート状況について at SE の雑記

相変わらず System.Data.SqlClient v4.4.2 では例外出るんですが、 v4.5 の preview では動くらしいので、v4.5 の preview で試してみました。
dotnet-core - System.Data.SqlClient 4.5.0-preview2-25707-02 - MyGet - Hosting your NuGet, npm, Bower, Maven, PHP Composer and Vsix packages

Install-Package System.Data.SqlClient -Version 4.5.0-preview2-25707-02 -Source https://dotnet.myget.org/F/dotnet-core/api/v3/index.json 

これで動かすと System.Diagnostics.DiagnosticSource のバージョンが違うって例外が出ますが、System.Diagnostics.DiagnosticSource のバージョンも上げてあげると動きました。

Install-Package System.Diagnostics.DiagnosticSource -Version 4.5.0-preview2-25707-02 -Source https://dotnet.myget.org/F/dotnet-core/api/v3/index.json 

これでちゃんとトランザクション効いてる動きしてます。

Dependencies 見ると他にも preview なのありますが、この程度のソースなら DiagnosticSource だけ preview にしたらいけるみたいです。

using System;
using System.Data.SqlClient;
using System.Transactions;

namespace ConsoleApp3
{
    class Program
    {
        static void Main(string[] args)
        {
            var connstr = @"~";
            using (var tran = new TransactionScope())
            using (var conn = new SqlConnection(connstr))
            {
                conn.Open();
                // 何か更新
                // 更新したデータ引っ張てくる
                // tran.Complete(); ロールバックされることを確認する
            }
        }
    }
}

というわけで v4.5 待ちですねー。
ほんと早く使わせてくれ。。

SQL Server のグラフで巡回セールスマン問題をやってみる

SQL Server のグラフを色々試していくシリーズ。

2回目は巡回セールスマン問題(traveling salesman problem)です。
最短経路で目的地を全部回りたいってやつですね。

今回のお題はまた阪急電車です。
梅田からスタートして、

の全ての駅で1度は降りてから梅田に帰ってくる。
これの最安値経路を求めたいと思います。

雰囲気こんな感じになるかな?
梅田 -> 河原町 -> 高槻 -> 十三 -> 神戸三宮 -> 西宮北口 -> 宝塚 -> 梅田

また比較するために通常のテーブルでも試してみます。
相変わらず可視化は Neo4j で。
f:id:odashinsuke:20171227135649j:plain
今回は運賃なので、全ての駅が互いに繋がっている状態です。

ノードが駅でエッジが運賃です。

create table G_駅 (
    駅名 nvarchar(10) not null
) as node
create table G_運賃 (
    価格 int not null
) as edge

insert into G_駅 values 
(N'梅田'), (N'十三'), (N'西宮北口'), (N'神戸三宮'), (N'宝塚'), (N'高槻市'), (N'河原町')

declare @e1 nvarchar(max) = (select $node_id from G_駅 where 駅名 = N'梅田')
declare @e2 nvarchar(max) = (select $node_id from G_駅 where 駅名 = N'十三')
declare @e3 nvarchar(max) = (select $node_id from G_駅 where 駅名 = N'西宮北口')
declare @e4 nvarchar(max) = (select $node_id from G_駅 where 駅名 = N'神戸三宮')
declare @e5 nvarchar(max) = (select $node_id from G_駅 where 駅名 = N'宝塚')
declare @e6 nvarchar(max) = (select $node_id from G_駅 where 駅名 = N'高槻市')
declare @e7 nvarchar(max) = (select $node_id from G_駅 where 駅名 = N'河原町')

insert into G_運賃 values 
(@e1, @e2, 150), 
(@e1, @e3, 270), 
(@e1, @e4, 320), 
(@e1, @e5, 280), 
(@e1, @e6, 280), 
(@e1, @e7, 400), 
(@e2, @e1, 150), 
(@e2, @e3, 220), 
(@e2, @e4, 320), 
(@e2, @e5, 280), 
(@e2, @e6, 280), 
(@e2, @e7, 400),
(@e3, @e2, 220), 
(@e3, @e1, 270), 
(@e3, @e4, 270), 
(@e3, @e5, 190), 
(@e3, @e6, 370), 
(@e3, @e7, 470),
(@e4, @e2, 320), 
(@e4, @e3, 270), 
(@e4, @e1, 320), 
(@e4, @e5, 280), 
(@e4, @e6, 400), 
(@e4, @e7, 620), 
(@e5, @e2, 280), 
(@e5, @e3, 190), 
(@e5, @e4, 280), 
(@e5, @e1, 280), 
(@e5, @e6, 370), 
(@e5, @e7, 530), 
(@e6, @e2, 280), 
(@e6, @e3, 370), 
(@e6, @e4, 400), 
(@e6, @e5, 370), 
(@e6, @e1, 280), 
(@e6, @e7, 280), 
(@e7, @e2, 400), 
(@e7, @e3, 470), 
(@e7, @e4, 620), 
(@e7, @e5, 530), 
(@e7, @e6, 280), 
(@e7, @e1, 400)

ついでに、通常のテーブルの方も作ってしまいましょう。

create table 運賃 (
    発駅 nvarchar(10) not null, 
    着駅 nvarchar(10) not null, 
    価格 int not null
)

insert into 運賃 values 
(N'梅田', N'十三', 150), 
(N'梅田', N'西宮北口', 270), 
(N'梅田', N'神戸三宮', 320), 
(N'梅田', N'宝塚', 280), 
(N'梅田', N'高槻市', 280), 
(N'梅田', N'河原町', 400), 
(N'十三', N'梅田', 150), 
(N'十三', N'西宮北口', 220), 
(N'十三', N'神戸三宮', 320), 
(N'十三', N'宝塚', 280), 
(N'十三', N'高槻市', 280), 
(N'十三', N'河原町', 400),
(N'西宮北口', N'十三', 220), 
(N'西宮北口', N'梅田', 270), 
(N'西宮北口', N'神戸三宮', 270), 
(N'西宮北口', N'宝塚', 190), 
(N'西宮北口', N'高槻市', 370), 
(N'西宮北口', N'河原町', 470),
(N'神戸三宮', N'十三', 320), 
(N'神戸三宮', N'西宮北口', 270), 
(N'神戸三宮', N'梅田', 320), 
(N'神戸三宮', N'宝塚', 280), 
(N'神戸三宮', N'高槻市', 400), 
(N'神戸三宮', N'河原町', 620), 
(N'宝塚', N'十三', 280), 
(N'宝塚', N'西宮北口', 190), 
(N'宝塚', N'神戸三宮', 280), 
(N'宝塚', N'梅田', 280), 
(N'宝塚', N'高槻市', 370), 
(N'宝塚', N'河原町', 530), 
(N'高槻市', N'十三', 280), 
(N'高槻市', N'西宮北口', 370), 
(N'高槻市', N'神戸三宮', 400), 
(N'高槻市', N'宝塚', 370), 
(N'高槻市', N'梅田', 280), 
(N'高槻市', N'河原町', 280), 
(N'河原町', N'十三', 400), 
(N'河原町', N'西宮北口', 470), 
(N'河原町', N'神戸三宮', 620), 
(N'河原町', N'宝塚', 530), 
(N'河原町', N'高槻市', 280), 
(N'河原町', N'梅田', 400)

前回 はグラフのクエリで再帰を使ってしまったので、通常のテーブルとの違いがあんまり分かりませんでした。
今回は再帰を使わずに書いてみます。

こんな感じでしょうか。

select top(10)
  concat(開始梅田.駅名, '-', N1.駅名, '-', N2.駅名, '-', N3.駅名, '-', N4.駅名, '-', N5.駅名, '-', N6.駅名, '-', 終了梅田.駅名) as ルート, 
  E1.価格 + E2.価格 + E3.価格 + E4.価格 + E5.価格 + E6.価格 + E7.価格 as 価格 
from G_駅 as 開始梅田, 
  G_運賃 E1, G_駅 as N1, 
  G_運賃 E2, G_駅 as N2,
  G_運賃 E3, G_駅 as N3,
  G_運賃 E4, G_駅 as N4,
  G_運賃 E5, G_駅 as N5,
  G_運賃 E6, G_駅 as N6,
  G_運賃 E7, G_駅 as 終了梅田
where
  開始梅田.駅名 = N'梅田' and 終了梅田.駅名 = N'梅田'
  and match ( 開始梅田 - (E1) -> N1 - (E2) -> N2 - (E3) -> N3 - (E4) -> N4 - (E5) -> N5 - (E6) -> N6 - (E7) -> 終了梅田)
  and N1.駅名 <> 開始梅田.駅名
  and N2.駅名 <> 開始梅田.駅名
  and N2.駅名 <> N1.駅名
  and N3.駅名 <> 開始梅田.駅名
  and N3.駅名 <> N1.駅名
  and N3.駅名 <> N2.駅名
  and N4.駅名 <> 開始梅田.駅名
  and N4.駅名 <> N1.駅名
  and N4.駅名 <> N2.駅名
  and N4.駅名 <> N3.駅名
  and N5.駅名 <> 開始梅田.駅名
  and N5.駅名 <> N1.駅名
  and N5.駅名 <> N2.駅名
  and N5.駅名 <> N3.駅名
  and N5.駅名 <> N4.駅名
  and N6.駅名 <> 開始梅田.駅名
  and N6.駅名 <> N1.駅名
  and N6.駅名 <> N2.駅名
  and N6.駅名 <> N3.駅名
  and N6.駅名 <> N4.駅名
  and N6.駅名 <> N5.駅名
order by 2

f:id:odashinsuke:20171227141113j:plain
開始の梅田と終了の梅田は置いといて、その間を 6つのノード(駅)と7つのエッジ(運賃)で繋げていく。
6つのノードは、今までに出てきた駅名は除くという条件を延々と付けていくと重複無しの移動ルートが取れます。
※ちなみにデータ上、発駅と着駅が同じになるデータ*1は入れていないので、一つ前の駅と同じ*2という条件は省けますが意味の伝わりやすさの観点から入れてます。

その中で、運賃.価格の合計を安い順に並べて Top で引っ張るっという感じです。
愚直に書いてますが、まあ意味は分かりやすいかな?

通常のテーブルはこちら。

select top(10)
  concat(開始梅田.発駅, '-', T1.発駅, '-', T2.発駅, '-', T3.発駅, '-', T4.発駅, '-', T5.発駅, '-', 終了梅田.発駅, '-', 終了梅田.着駅) as ルート, 
  開始梅田.価格 + T1.価格 + T2.価格 + T3.価格 + T4.価格 + T5.価格 + 終了梅田.価格 as 価格 
from
  運賃 開始梅田 
  inner join 運賃 T1 on 開始梅田.着駅 = T1.発駅 
  inner join 運賃 T2 on T1.着駅 = T2.発駅
  inner join 運賃 T3 on T2.着駅 = T3.発駅 
  inner join 運賃 T4 on T3.着駅 = T4.発駅 
  inner join 運賃 T5 on T4.着駅 = T5.発駅
  inner join 運賃 終了梅田 on T5.着駅 = 終了梅田.発駅
where
  開始梅田.発駅 = N'梅田' and 終了梅田.着駅 = N'梅田'
  and 開始梅田.着駅 <> N'梅田'
  and T1.着駅 <> 開始梅田.発駅
  and T2.着駅 <> 開始梅田.発駅
  and T2.着駅 <> T1.発駅
  and T3.着駅 <> 開始梅田.発駅
  and T3.着駅 <> T1.発駅
  and T3.着駅 <> T2.発駅
  and T4.着駅 <> 開始梅田.発駅
  and T4.着駅 <> T1.発駅
  and T4.着駅 <> T2.発駅
  and T4.着駅 <> T3.発駅
  and T5.着駅 <> 開始梅田.発駅
  and T5.着駅 <> T1.発駅
  and T5.着駅 <> T2.発駅
  and T5.着駅 <> T3.発駅
  and T5.着駅 <> T4.発駅
order by 2

f:id:odashinsuke:20171227141500j:plain
match の部分が inner join on に置き換わるイメージですね。
パッと見的には、グラフの match の方が繋がりは分かりやすいかな?
ただ、グラフの場合は From で指定するテーブルが多くなるのはイマイチですね。

通常のテーブルのみですが、再帰版も置いときます。

with cte as (
select 発駅, 着駅, 価格, cast(concat(発駅, '-', 着駅) as nvarchar(max)) as ルート, 1 as cnt from 運賃 where 発駅 = N'梅田'
union all
select 運賃.発駅, 運賃.着駅, cte.価格 + 運賃.価格, cast(concat(cte.ルート, '-', 運賃.着駅) as nvarchar(max)), cte.cnt + 1
from cte inner join 運賃 on cte.着駅 = 運賃.発駅 and charindex(運賃.着駅, cte.ルート) = 0
)
select 
  concat(cte.ルート, '-', 運賃.着駅) as ルート
  , cte.価格 + 運賃.価格 as 合計金額 
from 
  cte inner join 運賃 on cte.着駅 = 運賃.発駅 and 運賃.着駅 = N'梅田' 
where cnt = 6  
order by 2

再帰を使う場合は、最後の梅田を再帰外にしています、梅田だけ2回通る(最初と最後)を表現するのが大変だったので。
今回も試せる環境は用意しましたので、興味ある方はクエリ書いて試してください。 TSQL Runner Q20180123
一応 Neo4j の cypher も貼っときますが、梅田だけ2回通るのを表現出来なくてズルしてます。
梅田=>~(他の駅全て)=>十三=>梅田 という取り方になってます。
上手く cypher で表現出来る方いたら教えてください~。
データ

create (s1:F_Station {name:"梅田"}), 
  (s2:F_Station {name:"十三"}), 
  (s3:F_Station {name:"西宮北口"}), 
  (s4:F_Station {name:"神戸三宮"}), 
  (s5:F_Station {name:"宝塚"}), 
  (s6:F_Station {name:"高槻市"}), 
  (s7:F_Station {name:"河原町"}),
  (s1)-[:From {cost:150}]->(s2), 
  (s1)-[:From {cost:270}]->(s3),
  (s1)-[:From {cost:320}]->(s4),
  (s1)-[:From {cost:280}]->(s5),
  (s1)-[:From {cost:280}]->(s6),
  (s1)-[:From {cost:400}]->(s7),
  (s2)-[:From {cost:150}]->(s1), 
  (s2)-[:From {cost:220}]->(s3),
  (s2)-[:From {cost:320}]->(s4),
  (s2)-[:From {cost:280}]->(s5),
  (s2)-[:From {cost:280}]->(s6),
  (s2)-[:From {cost:400}]->(s7),
  (s3)-[:From {cost:220}]->(s2), 
  (s3)-[:From {cost:270}]->(s1),
  (s3)-[:From {cost:270}]->(s4),
  (s3)-[:From {cost:190}]->(s5),
  (s3)-[:From {cost:370}]->(s6),
  (s3)-[:From {cost:470}]->(s7),
  (s4)-[:From {cost:320}]->(s2), 
  (s4)-[:From {cost:270}]->(s3),
  (s4)-[:From {cost:320}]->(s1),
  (s4)-[:From {cost:280}]->(s5),
  (s4)-[:From {cost:400}]->(s6),
  (s4)-[:From {cost:620}]->(s7),
  (s5)-[:From {cost:280}]->(s2), 
  (s5)-[:From {cost:190}]->(s3),
  (s5)-[:From {cost:280}]->(s4),
  (s5)-[:From {cost:280}]->(s1),
  (s5)-[:From {cost:370}]->(s6),
  (s5)-[:From {cost:530}]->(s7),
  (s6)-[:From {cost:280}]->(s2), 
  (s6)-[:From {cost:370}]->(s3),
  (s6)-[:From {cost:400}]->(s4),
  (s6)-[:From {cost:370}]->(s5),
  (s6)-[:From {cost:280}]->(s1),
  (s6)-[:From {cost:280}]->(s7),
  (s7)-[:From {cost:400}]->(s2), 
  (s7)-[:From {cost:470}]->(s3),
  (s7)-[:From {cost:620}]->(s4),
  (s7)-[:From {cost:530}]->(s5),
  (s7)-[:From {cost:280}]->(s6),
  (s7)-[:From {cost:400}]->(s1)
RETURN s1, s2, s3, s4, s5, s6, s7

梅田=>~=>十三=>梅田

MATCH (from:F_Station {name:"梅田"}), 
  path = (from)-[:From*6]->(:F_Station {name:"十三"}), 
  (:F_Station {name:"十三"})-[last:From]->(from)
WHERE ALL(n IN NODES(path) WHERE SINGLE(m IN NODES(path) WHERE m = n))
RETURN path, REDUCE(cost = 0, edge IN RELATIONSHIPS(path) | cost + edge.cost) + last.cost AS totalCost
ORDER BY totalCost ASC
LIMIT 1

*1:運賃的には0円?入場券で150円

*2:N2.駅名<>N1.駅名 はあり得ない