{"id":1324,"date":"2026-09-23T23:56:31","date_gmt":"2026-09-23T14:56:31","guid":{"rendered":"https:\/\/roundwide.com\/?p=1324"},"modified":"2026-09-28T22:40:23","modified_gmt":"2026-09-28T13:40:23","slug":"ef-core-in-memory-composite-key-join","status":"publish","type":"post","link":"https:\/\/roundwide.com\/ja\/ef-core-in-memory-composite-key-join\/","title":{"rendered":"EF Core\u3067\u30a4\u30f3\u30e1\u30e2\u30ea\u306e\u8907\u5408\u30ad\u30fc\u3068DB\u30ec\u30b3\u30fc\u30c9\u3092\u7167\u5408\u3059\u308b"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\u5916\u90e8API\u3084CSV\u304b\u3089\u53d6\u5f97\u3057\u305f\u30ad\u30fc\u306e\u4e00\u89a7\u3092\u4f7f\u3063\u3066\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u8a72\u5f53\u30ec\u30b3\u30fc\u30c9\u3092\u53d6\u5f97\u3057\u305f\u3044\u3053\u3068\u304c\u3042\u308b\u3002\u5358\u4e00\u5217\u306e\u30ad\u30fc\u306a\u3089<code>Contains<\/code>\u3067\u66f8\u3051\u308b\u304c\u3001\u8907\u5408\u30ad\u30fc\u306b\u306a\u308b\u3068\u5c11\u3057\u5384\u4ecb\u3067\u3042\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u305f\u3068\u3048\u3070\u3001DB\u5074\u306e<code>DbRecord<\/code>\u3068\u30e1\u30e2\u30ea\u4e0a\u306e<code>CompositeKey<\/code>\u3092<code>Key1<\/code>\u3001<code>Key2<\/code>\u3067\u7d50\u5408\u3059\u308b\u3082\u306e\u3068\u3059\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">public sealed class DbRecord\n{\n    public int Key1 { get; set; }\n    public string Key2 { get; set; } = string.Empty;\n    public string Payload { get; set; } = string.Empty;\n}\npublic sealed record CompositeKey(int Key1, string Key2);<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u7d20\u76f4\u306b\u66f8\u3051\u3070\u3001\u6b21\u306e\u3088\u3046\u306a<code>Join<\/code>\u306b\u306a\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">var records = await db.Records\n    .Join(\n        keys,\n        record => new { record.Key1, record.Key2 },\n        key => new { key.Key1, key.Key2 },\n        (record, _) => record)\n    .ToListAsync(cancellationToken);<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u3057\u304b\u3057\u3001<code>db.Records<\/code>\u306fSQL\u3078\u5909\u63db\u3055\u308c\u308b<code>IQueryable<\/code>\u3067\u3042\u308b\u306e\u306b\u5bfe\u3057\u3001<code>keys<\/code>\u306f\u30e1\u30e2\u30ea\u4e0a\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u96c6\u5408\u3067\u3042\u308b\u3002\u3053\u306e\u4e21\u8005\u3092\u8907\u5408\u30ad\u30fc\u3067\u7d50\u5408\u3059\u308b<code>Join<\/code>\u306fSQL\u3078\u5909\u63db\u3067\u304d\u305a\u3001\u5b9f\u884c\u6642\u306b\u4f8b\u5916\u3068\u306a\u308b\u3002\u5148\u306b<code>AsEnumerable<\/code>\u3092\u547c\u3079\u3070LINQ to Objects\u3068\u3057\u3066\u7d50\u5408\u3067\u304d\u308b\u304c\u3001\u305d\u306e\u305f\u3081\u306b\u306f\u7167\u5408\u524d\u306eDB\u30ec\u30b3\u30fc\u30c9\u3092\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3078\u8aad\u307f\u8fbc\u307e\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u7167\u5408\u3092DB\u5074\u3067\u5b9f\u884c\u3059\u308b\u306b\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092SQL\u3078\u6e21\u305b\u308b\u5f62\u5f0f\u306b\u5909\u63db\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3002\u3053\u306e\u8a18\u4e8b\u3067\u306f\u3001\u305d\u306e\u305f\u3081\u306b\u5b9f\u88c5\u3057\u305f\u6b21\u306e2\u65b9\u5f0f\u3092\u6bd4\u8f03\u3059\u308b\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092<code>WHERE<\/code>\u6761\u4ef6\u3078\u5c55\u958b\u3059\u308b<code>CompositeKeyPredicateBuilder<\/code><\/li>\n\n\n\n<li>\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092JSON\u3067\u6e21\u3057\u3001SQL Server\u306e<code>OPENJSON<\/code>\u3067\u884c\u30bb\u30c3\u30c8\u306b\u5c55\u958b\u3059\u308b<code>OpenJsonQueryBuilder<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u30b3\u30fc\u30c9\u306f\u8981\u70b9\u3060\u3051\u3092\u629c\u7c8b\u3057\u3066\u3044\u308b\u3002\u5b8c\u5168\u306a\u5b9f\u88c5\u3068\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u4e00\u5f0f\u306f<a href=\"https:\/\/github.com\/fujieda\/EfCoreCompositeKeyJoin\">GitHub\u30ea\u30dd\u30b8\u30c8\u30ea<\/a>\u3067\u516c\u958b\u3057\u3066\u3044\u308b\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5f0f\u6728\u3067<code>WHERE<\/code>\u6761\u4ef6\u3078\u5c55\u958b\u3059\u308b<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">1\u3064\u76ee\u306e<code>CompositeKeyPredicateBuilder<\/code>\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u5217\u3054\u3068\u306e\u6bd4\u8f03\u6761\u4ef6\u3092AND\u3067\u3064\u306a\u304e\u3001\u3055\u3089\u306b\u8907\u5408\u30ad\u30fc\u306e\u884c\u3054\u3068\u306e\u6761\u4ef6\u3092OR\u3067\u3064\u306a\u3044\u3067\u30011\u3064\u306e\u8ff0\u8a9e\u3092\u751f\u6210\u3059\u308b\u3002\u8907\u5408\u30ad\u30fc\u30922\u884c\u6e21\u3057\u305f\u5834\u5408\u3001\u6b21\u306e\u3088\u3046\u306a\u6761\u4ef6\u3092\u751f\u6210\u3059\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">record =>\n    (record.Key1 == 1 && record.Key2 == \"K00001\") ||\n    (record.Key1 == 2 && record.Key2 == \"K00002\")<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u306e\u5f62\u306e\u5f0f\u306a\u3089\u3001EF Core\u306f\u901a\u5e38\u306e<code>Where<\/code>\u6761\u4ef6\u3068\u3057\u3066SQL\u3078\u5909\u63db\u3067\u304d\u308b\u3002\u547c\u3073\u51fa\u3057\u5074\u306f\u3001\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u5074\u3068\u5165\u529b\u30ad\u30fc\u5074\u306e\u5bfe\u5fdc\u3059\u308b\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u6307\u5b9a\u3057\u3001\u751f\u6210\u3055\u308c\u305f\u8ff0\u8a9e\u3092\u6e21\u3059\u3060\u3051\u3067\u3042\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">var builder = new CompositeKeyPredicateBuilder<DbRecord, CompositeKey>(\n    [nameof(DbRecord.Key1), nameof(DbRecord.Key2)],\n    [nameof(CompositeKey.Key1), nameof(CompositeKey.Key2)]);\nvar predicate = builder.Build(keys);\nvar records = await db.Records\n    .Where(predicate)\n    .ToListAsync(cancellationToken);<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u30d3\u30eb\u30c0\u30fc\u306e\u4e2d\u5fc3\u90e8\u5206\u3067\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u5404\u5217\u306b\u5bfe\u5fdc\u3059\u308b\u6bd4\u8f03\u5f0f\u3092\u4f5c\u308a\u3001\u305d\u308c\u3089\u3092\u884c\u3054\u3068\u306b1\u3064\u306e<code>Expression&lt;Func&lt;TEntity, bool&gt;&gt;<\/code>\u3078\u307e\u3068\u3081\u3066\u3044\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">public Expression<Func<TEntity, bool>> Build(IEnumerable<TKey> keys)\n{\n    ArgumentNullException.ThrowIfNull(keys);\n    var entityParameter = Expression.Parameter(typeof(TEntity), \"entity\");\n    var matches = new List<Expression>();\n    foreach (var key in keys)\n    {\n        ArgumentNullException.ThrowIfNull(key);\n        var comparisons = new Expression[_entityProperties.Length];\n        for (var index = 0; index < _entityProperties.Length; index++)\n        {\n            var property = _entityProperties[index];\n            var propertyAccess = Expression.Property(entityParameter, property);\n            var constant = Expression.Constant(_keyGetters[index](key), property.PropertyType);\n            comparisons[index] = Expression.Equal(propertyAccess, constant);\n        }\n        matches.Add(BuildBalancedAnd(comparisons, 0, comparisons.Length));\n    }\n    var predicate = BuildBalancedOr(matches, 0, matches.Count)\n        ?? Expression.Constant(false);\n    return Expression.Lambda<Func<TEntity, bool>>(predicate, entityParameter);\n}<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u8907\u5408\u30ad\u30fc\u306e\u884c\u304c0\u4ef6\u306e\u5834\u5408\u306f\u5e38\u306b<code>false<\/code>\u3068\u306a\u308b\u8ff0\u8a9e\u3092\u751f\u6210\u3059\u308b\u305f\u3081\u3001\u547c\u3073\u51fa\u3057\u5074\u306b\u7279\u5225\u306a\u5206\u5c90\u3092\u8a2d\u3051\u308b\u5fc5\u8981\u306f\u306a\u3044\u3002\u307e\u305f\u3001\u30d7\u30ed\u30d1\u30c6\u30a3\u6570\u306e\u4e0d\u4e00\u81f4\u3001\u5b58\u5728\u3057\u306a\u3044\u30d7\u30ed\u30d1\u30c6\u30a3\u540d\u3001\u578b\u306e\u4e0d\u4e00\u81f4\u3068\u3044\u3063\u305f\u8a2d\u5b9a\u8aa4\u308a\u306f\u3001\u30d3\u30eb\u30c0\u30fc\u306e\u69cb\u7bc9\u6642\u306b\u4f8b\u5916\u3068\u3057\u3066\u691c\u51fa\u3059\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5f0f\u6728\u65b9\u5f0f\u306fSQL Server\u56fa\u6709\u306e\u69cb\u6587\u3092\u4f7f\u308f\u305a\u3001\u65e2\u5b58\u306eLINQ\u3078\u7d44\u307f\u8fbc\u307f\u3084\u3059\u3044\u3002\u4e00\u65b9\u3001\u8907\u5408\u30ad\u30fc\u306e\u5404\u884c\u306b\u3064\u3044\u3066\u3001\u8907\u5408\u30ad\u30fc\u306e\u5217\u3054\u3068\u306b\u6bd4\u8f03\u6761\u4ef6\u3092\u751f\u6210\u3059\u308b\u3002\u305d\u306e\u305f\u3081\u3001\u5f0f\u3068SQL\u306e\u5927\u304d\u3055\u306f\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u00d7\u8907\u5408\u30ad\u30fc\u306e\u5217\u6570\u306b\u6bd4\u4f8b\u3057\u3001\u5f0f\u306e\u751f\u6210\u3001SQL\u3078\u306e\u5909\u63db\u3001\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066\u306e\u30b3\u30b9\u30c8\u3082\u5897\u3048\u308b\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092JSON\u3067\u6e21\u3057\u3001<code>OPENJSON<\/code>\u3067\u5c55\u958b\u3059\u308b<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">2\u3064\u76ee\u306e<code>OpenJsonQueryBuilder<\/code>\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092JSON\u914d\u5217\u306b\u3057\u30661\u3064\u306eSQL\u30d1\u30e9\u30e1\u30fc\u30bf\u3067\u6e21\u3059\u3002SQL Server\u5074\u3067\u306f<code>OPENJSON<\/code>\u306b\u3088\u3063\u3066JSON\u3092\u884c\u30bb\u30c3\u30c8\u306b\u5c55\u958b\u3057\u3001\u5143\u306e\u30c6\u30fc\u30d6\u30eb\u3068JOIN\u3059\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u751f\u6210\u3055\u308c\u308bSQL\u306f\u6b21\u306e\u3088\u3046\u306b\u306a\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"language-sql\">SELECT r.[Key1], r.[Key2], r.[Payload]\nFROM [dbo].[DbRecords] AS r\nINNER JOIN (\n    SELECT DISTINCT [Key1], [Key2]\n    FROM OPENJSON(@json)\n    WITH (\n        [Key1] int '$.Key1',\n        [Key2] varchar(50) '$.Key2'\n    )\n) AS inputKeys\n    ON r.[Key1] = inputKeys.[Key1]\n    AND r.[Key2] = inputKeys.[Key2]<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u547c\u3073\u51fa\u3057\u5074\u306f\u3001\u5f0f\u6728\u65b9\u5f0f\u3068\u540c\u3058\u304f\u30b3\u30f3\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u3067\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u5074\u3068\u5165\u529b\u30ad\u30fc\u5074\u306e\u5bfe\u5fdc\u3059\u308b\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u6307\u5b9a\u3059\u308b\u3002\u305f\u3060\u3057\u3001\u30af\u30a8\u30ea\u306e\u7d44\u307f\u7acb\u3066\u65b9\u306f\u5f0f\u6728\u65b9\u5f0f\u3068\u306f\u7570\u306a\u308a\u3001<code>DbSet<\/code>\u3068\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092<code>Build<\/code>\u3078\u6e21\u3057<code>IQueryable<\/code>\u3092\u76f4\u63a5\u53d7\u3051\u53d6\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">var builder = new OpenJsonQueryBuilder<DbRecord, CompositeKey>(\n    [nameof(DbRecord.Key1), nameof(DbRecord.Key2)],\n    [nameof(CompositeKey.Key1), nameof(CompositeKey.Key2)]);\nvar records = await builder\n    .Build(db.Records, keys)\n    .ToListAsync(cancellationToken);<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Build<\/code>\u306f\u3001\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u306e\u578b\u3001\u30c6\u30fc\u30d6\u30eb\u3001\u30b9\u30ad\u30fc\u30de\u3001\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u5bfe\u5fdc\u95a2\u4fc2\u3054\u3068\u306bSQL\u6587\u3092\u521d\u56de\u5229\u7528\u6642\u306b\u751f\u6210\u3057\u3066\u30ad\u30e3\u30c3\u30b7\u30e5\u3057\u3001\u4ee5\u5f8c\u306e\u547c\u3073\u51fa\u3057\u3067\u518d\u5229\u7528\u3059\u308b\u3002\u8907\u5408\u30ad\u30fc\u306e\u884c\u306f\u547c\u3073\u51fa\u3057\u306e\u305f\u3073\u306bJSON\u3078\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3057\u3001SQL\u30d1\u30e9\u30e1\u30fc\u30bf\u3068\u3057\u3066\u30ad\u30e3\u30c3\u30b7\u30e5\u6e08\u307f\u306eSQL\u3068\u3068\u3082\u306b<code>FromSqlRaw<\/code>\u3078\u6e21\u3059\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">public IQueryable<TEntity> Build(\n    DbSet<TEntity> source,\n    IReadOnlyCollection<TKey> keys)\n{\n    ArgumentNullException.ThrowIfNull(source);\n    ArgumentNullException.ThrowIfNull(keys);\n    foreach (var key in keys)\n    {\n        ArgumentNullException.ThrowIfNull(key);\n    }\n    var shape = SqlShapes.GetOrAdd(\n        GetShapeKey(source.EntityType),\n        _ => CreateSql(source.EntityType));\n    var json = JsonSerializer.Serialize(keys);\n    var parameter = new SqlParameter(\"@json\", json);\n    return source.FromSqlRaw(shape, parameter);\n}<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u751f\u6210\u3059\u308bSQL\u306b\u306f\u3001EF Core\u306e\u30e2\u30c7\u30eb\u304b\u3089\u53d6\u5f97\u3057\u305f\u5b9f\u969b\u306e\u30c6\u30fc\u30d6\u30eb\u540d\u3001\u5217\u540d\u3001SQL\u578b\u3092\u4f7f\u3046\u3002\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u5074\u306e\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u5217\u306b\u30de\u30c3\u30d4\u30f3\u30b0\u3055\u308c\u3066\u3044\u306a\u3044\u3001\u5165\u529b\u30ad\u30fc\u5074\u306b\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u306a\u3044\u3001CLR\u578b\u304c\u4e00\u81f4\u3057\u306a\u3044\u3001\u3068\u3044\u3063\u305f\u8a2d\u5b9a\u8aa4\u308a\u306f\u30d3\u30eb\u30c0\u30fc\u304c\u691c\u51fa\u3059\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>CreateSql<\/code>\u3067\u306f\u3001\u691c\u8a3c\u6e08\u307f\u306e\u30d7\u30ed\u30d1\u30c6\u30a3\u304b\u3089<code>SELECT<\/code>\u5217\u3001<code>OPENJSON<\/code>\u306e\u5165\u529b\u5217\u3001JOIN\u6761\u4ef6\u3092\u305d\u308c\u305e\u308c\u751f\u6210\u3057\u30011\u3064\u306eSQL\u6587\u306b\u57cb\u3081\u8fbc\u3080\u3002\u8907\u5408\u30ad\u30fc\u306e\u884c\u304c\u91cd\u8907\u3057\u305f\u5834\u5408\u3082\u7d50\u679c\u3092\u5f0f\u6728\u65b9\u5f0f\u3068\u305d\u308d\u3048\u308b\u305f\u3081\u3001JOIN\u524d\u306e\u884c\u30bb\u30c3\u30c8\u306b<code>SELECT DISTINCT<\/code>\u3092\u9069\u7528\u3057\u3066\u3044\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">var selectColumns = string.Join(\", \", entityType.GetProperties()\n    .Select(property => $\"r.{QuoteIdentifier(property.GetColumnName(storeObject))}\"));\nvar inputColumns = string.Join(\",\\n\", entityProperties.Zip(keyProperties).Select(pair =>\n    $\"        {QuoteIdentifier(pair.Second.Name)} {GetSqlType(pair.First, storeObject)} '$.{pair.Second.Name}'\"));\nvar inputSelectColumns = string.Join(\", \", keyProperties\n    .Select(property => QuoteIdentifier(property.Name)));\nvar joinConditions = string.Join(\"\\n    AND \", entityProperties.Zip(keyProperties).Select(pair =>\n    $\"r.{QuoteIdentifier(pair.First.GetColumnName(storeObject))} = inputKeys.{QuoteIdentifier(pair.Second.Name)}\"));\nreturn $\"\"\"\n    SELECT {selectColumns}\n    FROM {QuoteIdentifier(entityType.GetSchema() ?? \"dbo\")}.{QuoteIdentifier(tableName)} AS r\n    INNER JOIN (\n        SELECT DISTINCT {inputSelectColumns}\n        FROM OPENJSON(@json)\n        WITH (\n    {inputColumns}\n        )\n    ) AS inputKeys\n        ON {joinConditions}\n    \"\"\";<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u5f0f\u6728\u65b9\u5f0f\u3068\u7570\u306a\u308a<code>OPENJSON<\/code>\u65b9\u5f0f\u3067\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u304c\u5897\u3048\u3066\u3082\u5897\u3048\u308b\u306e\u306fJSON\u914d\u5217\u306e\u8981\u7d20\u3060\u3051\u3067\u3042\u308b\u3002SQL\u6587\u81ea\u4f53\u306f\u5909\u308f\u3089\u305a\u3001\u30d1\u30e9\u30e1\u30fc\u30bf\u3082JSON\u6587\u5b57\u52171\u3064\u306e\u307e\u307e\u3067\u3042\u308b\u3002\u8907\u5408\u30ad\u30fc\u306e\u5217\u304c1\u3064\u5897\u3048\u308b\u3068\u3001<code>OPENJSON<\/code>\u306e<code>WITH<\/code>\u53e5\u306e\u5217\u5b9a\u7fa9\u3068JOIN\u306e\u6bd4\u8f03\u6761\u4ef6\u304c1\u3064\u305a\u3064\u5897\u3048\u308b\u304c\u3001\u3053\u306e\u5897\u52a0\u306f\u884c\u6570\u306b\u306f\u5de6\u53f3\u3055\u308c\u306a\u3044\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>OPENJSON<\/code>\u65b9\u5f0f\u306fSQL Server\u56fa\u6709\u306e\u69cb\u6587\u3068\u751fSQL\u306b\u4f9d\u5b58\u3059\u308b\u305f\u3081\u3001\u4ed6\u306eDB\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3078\u305d\u306e\u307e\u307e\u79fb\u690d\u306f\u3067\u304d\u306a\u3044\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u30d9\u30f3\u30c1\u30de\u30fc\u30af<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">2\u65b9\u5f0f\u3092\u6b21\u306e\u74b0\u5883\u3067\u6e2c\u5b9a\u3057\u305f\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>.NET 10 \/ EF Core 10.0.0<\/li>\n\n\n\n<li>SQL Server 2022<\/li>\n\n\n\n<li>BenchmarkDotNet 0.15.6<\/li>\n\n\n\n<li>\u8907\u5408\u30ad\u30fc\u306e\u5217\u306e\u578b\u306f<code>int<\/code>\u3068<code>varchar(50)<\/code><\/li>\n\n\n\n<li>DB\u30ec\u30b3\u30fc\u30c9\u6570\u306f100,000\u4ef6\u3067\u56fa\u5b9a<\/li>\n\n\n\n<li>\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u306f100\u30011,000\u300110,000<\/li>\n\n\n\n<li>\u8ffd\u8de1\u51e6\u7406\u306e\u5f71\u97ff\u3092\u9664\u304f\u305f\u3081\u3001\u4e21\u65b9\u5f0f\u3068\u3082<code>AsNoTracking<\/code>\u3092\u4f7f\u7528<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">DB\u306b\u306f100,000\u4ef6\u306e\u30ec\u30b3\u30fc\u30c9\u3092\u7528\u610f\u3057\u3001\u8907\u5408\u30ad\u30fc\u306e\u5404\u884c\u306f\u30c6\u30fc\u30d6\u30eb\u5168\u4f53\u304b\u3089\u5747\u7b49\u306b\u9078\u3093\u3067\u3044\u308b\u3002\u3059\u3079\u3066\u306e\u8907\u5408\u30ad\u30fc\u306b\u4e00\u81f4\u3059\u308b\u30ec\u30b3\u30fc\u30c9\u304c1\u4ef6\u305a\u3064\u5b58\u5728\u3059\u308b\u3002\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u30e1\u30bd\u30c3\u30c9\u306f\u3001\u30af\u30a8\u30ea\u3067\u53d6\u5f97\u3057\u305f<code>List&lt;DbRecord&gt;<\/code>\u3092\u305d\u306e\u307e\u307e\u8fd4\u3059\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">[Params(100, 1_000, 10_000)]\npublic int KeyCount { get; set; }\n[Benchmark(Baseline = true)]\npublic List&lt;DbRecord&gt; ExpressionTree() =>\n    JoinQueries.ExecuteExpressionTreeAsync(_db, _keys)\n    .GetAwaiter().GetResult();\n[Benchmark]\npublic List&lt;DbRecord&gt; OpenJson() =>\n    JoinQueries.ExecuteOpenJsonAsync(_db, _keys)\n    .GetAwaiter().GetResult();<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u304b\u3089\u547c\u3073\u51fa\u30592\u3064\u306e\u30e1\u30bd\u30c3\u30c9\u306f\u3001\u6b21\u306e\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u3059\u308b\u3002\u3069\u3061\u3089\u3082<code>AsNoTracking<\/code>\u3092\u9069\u7528\u3057\u3066\u304b\u3089\u7d50\u679c\u3092\u53d6\u5f97\u3057\u3066\u3044\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"lang-csharp\">public static Task&lt;List&lt;DbRecord&gt;&gt; ExecuteExpressionTreeAsync(\n    AppDbContext db,\n    IReadOnlyCollection&lt;CompositeKey&gt; keys,\n    CancellationToken cancellationToken = default)\n{\n    var predicate = CompositeKeyBuilder.Build(keys);\n    return db.Records\n        .AsNoTracking()\n        .Where(predicate)\n        .ToListAsync(cancellationToken);\n}\n\npublic static Task&lt;List&lt;DbRecord&gt;&gt; ExecuteOpenJsonAsync(\n    AppDbContext db,\n    IReadOnlyCollection&lt;CompositeKey&gt; keys,\n    CancellationToken cancellationToken = default) =&gt;\n    OpenJsonBuilder.Build(db.Records, keys)\n        .AsNoTracking()\n        .ToListAsync(cancellationToken);<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u7d50\u679c\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3042\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u884c\u6642\u9593\u306f\u5e73\u5747\u5024 \u00b1 \u6a19\u6e96\u504f\u5dee\u3067\u793a\u3057\u3066\u3044\u308b\u3002<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th class=\"has-text-align-right\" data-align=\"right\">\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570<\/th><th class=\"has-text-align-right\" data-align=\"right\">\u5f0f\u6728<\/th><th class=\"has-text-align-right\" data-align=\"right\"><code>OPENJSON<\/code><\/th><th class=\"has-text-align-right\" data-align=\"right\">\u5f0f\u6728\u306e\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066<\/th><th class=\"has-text-align-right\" data-align=\"right\"><code>OPENJSON<\/code>\u306e\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-right\" data-align=\"right\">100<\/td><td class=\"has-text-align-right\" data-align=\"right\">1.613 \u00b1 0.053 ms<\/td><td class=\"has-text-align-right\" data-align=\"right\">1.536 \u00b1 0.045 ms<\/td><td class=\"has-text-align-right\" data-align=\"right\">116.2 KB<\/td><td class=\"has-text-align-right\" data-align=\"right\">58.52 KB<\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">1,000<\/td><td class=\"has-text-align-right\" data-align=\"right\">6.222 \u00b1 0.072 ms<\/td><td class=\"has-text-align-right\" data-align=\"right\">4.927 \u00b1 0.086 ms<\/td><td class=\"has-text-align-right\" data-align=\"right\">1,086.5 KB<\/td><td class=\"has-text-align-right\" data-align=\"right\">436.2 KB<\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">10,000<\/td><td class=\"has-text-align-right\" data-align=\"right\">79.269 \u00b1 1.278 ms<\/td><td class=\"has-text-align-right\" data-align=\"right\">49.578 \u00b1 0.583 ms<\/td><td class=\"has-text-align-right\" data-align=\"right\">11,006.13 KB<\/td><td class=\"has-text-align-right\" data-align=\"right\">4,330.59 KB<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">100\u884c\u3067\u306f\u3001<code>OPENJSON<\/code>\u65b9\u5f0f\u306e\u5b9f\u884c\u6642\u9593\u306f\u5f0f\u6728\u65b9\u5f0f\u3088\u308a0.077 ms\u77ed\u3044\u3060\u3051\u3067\u5fae\u5dee\u3060\u304c\u3001\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066\u91cf\u306f\u7d0450%\u3067\u3042\u308b\u30021,000\u884c\u3067\u306f\u3001\u5b9f\u884c\u6642\u9593\u306e\u5dee\u306f1.295 ms\u306b\u5e83\u304c\u308a\u3001<code>OPENJSON<\/code>\u65b9\u5f0f\u306e\u5b9f\u884c\u6642\u9593\u306f\u5f0f\u6728\u65b9\u5f0f\u306e\u7d0479%\u3001\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066\u91cf\u306f\u7d0440%\u3068\u306a\u3063\u305f\u300210,000\u884c\u3067\u306f\u3001\u5b9f\u884c\u6642\u9593\u306e\u5dee\u306f29.691 ms\u307e\u3067\u5e83\u304c\u308a\u3001<code>OPENJSON<\/code>\u65b9\u5f0f\u306e\u5b9f\u884c\u6642\u9593\u306f\u5f0f\u6728\u65b9\u5f0f\u306e\u7d0463%\u3001\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066\u91cf\u306f\u7d0439%\u3067\u3042\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u306e\u7d50\u679c\u306f\u3001Windows 11\uff0810.0.26200.9457\uff09\u3001AMD Ryzen 7 3800X\uff088\u30b3\u30a2\u300116\u30b9\u30ec\u30c3\u30c9\uff09\u3001.NET SDK 10.0.400\u3001.NET 10.0.11 x64\u306e\u74b0\u5883\u3067\u3001BenchmarkDotNet 0.15.6\u306eLongRun\u30b8\u30e7\u30d6\uff08\u8d77\u52d53\u56de\u3001\u30a6\u30a9\u30fc\u30e0\u30a2\u30c3\u30d715\u56de\u3001\u6e2c\u5b9a100\u56de\uff09\u3092\u4f7f\u3063\u3066\u6e2c\u5b9a\u3057\u305f\u3082\u306e\u3067\u3042\u308b\u3002SQL Server 2022\u306f\u540c\u3058PC\u4e0a\u306eDocker\u30b3\u30f3\u30c6\u30ca\u3067\u5b9f\u884c\u3057\u305f\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u3069\u3061\u3089\u3092\u4f7f\u3046\u304b<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u5224\u65ad\u57fa\u6e96\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u3068DB\u56fa\u6709\u306e\u5b9f\u88c5\u3092\u8a31\u5bb9\u3067\u304d\u308b\u304b\u3069\u3046\u304b\u3067\u3042\u308b\u3002<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>\u6761\u4ef6<\/th><th>\u9078\u3073\u3084\u3059\u3044\u65b9\u5f0f<\/th><\/tr><\/thead><tbody><tr><td>SQL Server\u4ee5\u5916\u3067\u3082\u4f7f\u3044\u305f\u3044<\/td><td>\u5f0f\u6728<\/td><\/tr><tr><td>\u901a\u5e38\u306eLINQ\u3068\u3057\u3066\u7d44\u307f\u7acb\u3066\u305f\u3044<\/td><td>\u5f0f\u6728<\/td><\/tr><tr><td>\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u304c\u5c11\u306a\u3044<\/td><td>\u5f0f\u6728<\/td><\/tr><tr><td>SQL Server\u306b\u56fa\u5b9a\u3067\u304d\u308b<\/td><td><code>OPENJSON<\/code><\/td><\/tr><tr><td>\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u304c\u591a\u3044<\/td><td><code>OPENJSON<\/code><\/td><\/tr><tr><td>SQL\u306e\u80a5\u5927\u5316\u3068\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066\u3092\u6291\u3048\u305f\u3044<\/td><td><code>OPENJSON<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u304c\u5c11\u306a\u3044\u5834\u5408\u306f\u3001\u5358\u7d14\u3067\u6271\u3044\u3084\u3059\u304f\u3001DB\u56fa\u6709\u306e\u30b3\u30fc\u30c9\u3082\u4e0d\u8981\u306a\u5f0f\u6728\u65b9\u5f0f\u304c\u9069\u3057\u3066\u3044\u308b\u3002\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u304c\u591a\u3044\u5834\u5408\u3001\u5229\u7528\u3059\u308bDB\u3092SQL Server\u306b\u9650\u5b9a\u3067\u304d\u308b\u306a\u3089\u3001\u884c\u6570\u304c\u5897\u3048\u3066\u3082SQL\u6587\u304c\u9577\u304f\u306a\u3089\u306a\u3044<code>OPENJSON<\/code>\u65b9\u5f0f\u304c\u6709\u529b\u3067\u3042\u308b\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b9f\u884c\u65b9\u6cd5<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u30ea\u30dd\u30b8\u30c8\u30ea\u306eDocker Compose\u69cb\u6210\u3092\u4f7f\u3048\u3070\u3001SQL Server\u3092\u8d77\u52d5\u3067\u304d\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"language-powerscript\">docker compose up -d<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">\u63a5\u7d9a\u6587\u5b57\u5217\u3092\u8a2d\u5b9a\u3057\u3001Release\u69cb\u6210\u3067\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u3092\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n\n<script type=\"text\/plain\" class=\"language-powerscript\">$env:EF_JOIN_CONNECTION_STRING = \"Server=localhost,14333;Database=EfCoreJoinBenchmark;User Id=sa;Password=Your_strong_password123;TrustServerCertificate=True;\"\ndotnet run --project .\\EfCoreJoinBenchmark.Benchmarks -c Release<\/script>\n\n\n\n<h2 class=\"wp-block-heading\">\u307e\u3068\u3081<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u30a4\u30f3\u30e1\u30e2\u30ea\u306e\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092<code>IQueryable<\/code>\u3078\u4e0a\u8a18\u306e\u3088\u3046\u306b\u76f4\u63a5<code>Join<\/code>\u3059\u308b\u3068\u3001EF Core\u306f\u305d\u306e\u30af\u30a8\u30ea\u3092SQL\u3078\u5909\u63db\u3067\u304d\u306a\u3044\u3002DB\u5074\u3067\u7167\u5408\u3059\u308b\u306b\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092SQL\u3078\u6e21\u305b\u308b\u5f62\u5f0f\u306b\u5909\u63db\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5f0f\u6728\u65b9\u5f0f\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u3092AND\u3068OR\u304b\u3089\u306a\u308b<code>Where<\/code>\u6761\u4ef6\u3078\u5909\u63db\u3059\u308b\u3002LINQ\u3068\u3057\u3066\u6271\u3044\u3084\u3059\u304fDB\u56fa\u6709\u306e\u6a5f\u80fd\u3092\u907f\u3051\u3089\u308c\u308b\u304c\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u6570\u00d7\u8907\u5408\u30ad\u30fc\u306e\u5217\u6570\u306b\u5fdc\u3058\u3066\u5f0f\u3068SQL\u304c\u5927\u304d\u304f\u306a\u308b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>OPENJSON<\/code>\u65b9\u5f0f\u306f\u3001\u8907\u5408\u30ad\u30fc\u306e\u884c\u30921\u3064\u306eJSON\u30d1\u30e9\u30e1\u30fc\u30bf\u3067\u9001\u308a\u3001SQL Server\u4e0a\u3067\u884c\u30bb\u30c3\u30c8\u306b\u5c55\u958b\u3057\u3066<code>Join<\/code>\u3059\u308b\u3002\u884c\u6570\u304c\u5897\u3048\u3066\u3082SQL\u6587\u306f\u5909\u308f\u3089\u305a\u3001\u8907\u5408\u30ad\u30fc\u306e\u5217\u304c\u5897\u3048\u308b\u3068\u3001\u305d\u306e\u5206\u3060\u3051\u5217\u5b9a\u7fa9\u3068\u6bd4\u8f03\u6761\u4ef6\u304c\u5897\u3048\u308b\u3002SQL Server\u306b\u4f9d\u5b58\u3059\u308b\u304c\u3001\u4eca\u56de\u306e\u6e2c\u5b9a\u3067\u306f\u884c\u6570\u304c\u591a\u3044\u307b\u3069\u5b9f\u884c\u6642\u9593\u3068\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066\u306e\u4e21\u65b9\u3067\u6709\u5229\u3067\u3042\u3063\u305f\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5916\u90e8API\u3084CSV\u304b\u3089\u53d6\u5f97\u3057\u305f\u30ad\u30fc\u306e\u4e00\u89a7\u3092\u4f7f\u3063\u3066\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u8a72\u5f53\u30ec\u30b3\u30fc\u30c9\u3092\u53d6\u5f97\u3057\u305f\u3044\u3053\u3068\u304c\u3042\u308b\u3002\u5358\u4e00\u5217\u306e\u30ad\u30fc\u306a\u3089Contains\u3067\u66f8\u3051\u308b\u304c\u3001\u8907\u5408\u30ad\u30fc\u306b\u306a\u308b\u3068\u5c11\u3057\u5384\u4ecb\u3067\u3042\u308b\u3002 \u305f\u3068\u3048\u3070\u3001DB\u5074\u306eDbRecord\u3068\u30e1\u30e2 &hellip; <a href=\"https:\/\/roundwide.com\/ja\/ef-core-in-memory-composite-key-join\/\" class=\"more-link\"><span class=\"screen-reader-text\">\"EF Core\u3067\u30a4\u30f3\u30e1\u30e2\u30ea\u306e\u8907\u5408\u30ad\u30fc\u3068DB\u30ec\u30b3\u30fc\u30c9\u3092\u7167\u5408\u3059\u308b\" \u306e<\/span>\u7d9a\u304d\u3092\u8aad\u3080<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"ja","_original_post":"https:\/\/roundwide.com\/?p=1324","footnotes":""},"categories":[1],"tags":[],"class_list":["post-1324","post","type-post","status-publish","format-standard","hentry","category-1","ja"],"_links":{"self":[{"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/posts\/1324","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/comments?post=1324"}],"version-history":[{"count":30,"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/posts\/1324\/revisions"}],"predecessor-version":[{"id":1396,"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/posts\/1324\/revisions\/1396"}],"wp:attachment":[{"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/media?parent=1324"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/categories?post=1324"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/roundwide.com\/wp-json\/wp\/v2\/tags?post=1324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}