none
抽出リストを使ったデータの抽出(結合?)方法が知りたいです。 RRS feed

  • 質問

  • お世話になります。

    .NET Framework でデータテーブルを扱おうとしているんですが、該当するフォーラムが見つけられなかったので、こちらで質問させて頂きました。

    やりたいのは、下記のような データテーブル (仮に名前 T1 とします) ↓

    [Name], [Value]

    "A", 3302

    "B", 1020

    "C", 1505

    "D", 700

    "E", 8941

    "ZZY",  1591

    "ZZZ",  3821

    ↑があるとして、

    この中から、抽出したい [Name] 行の一覧  (T2) ↓

    [Name2]

    "B"

    "D"

    "ZZX"

    "ZZZ"

    ↑ と照らし合わせて、T1 から、T2 に載っている [Name] の行だけ、抽出したいんです。

    結果は こんな感じです (T3) ↓

    [Name],[Value]

    "B", 1020

    "D", 700

    "ZZX", 977

    "ZZZ",  3821

    どなたか、これを SQL コマンドでやる方法 (と、もしご存知なら .NET Framework で実装する方法) を 教えて戴けないでしょうか?

    宜しくお願いします。

    2018年5月23日 10:19

回答

  • 何のひねりもありませんが…
    SELECT [Name], [Value]
    FROM T1
    WHERE [Name] IN (SELECT [Name2] FROM T2);

    2018年5月23日 12:48
  • 普通にinner joinで良いのでは?

    select Name, Value
    from T1
    inner join T2 on T1.Name = T2.Name

    (追記)
    と、もしご存知なら .NET Framework で実装する方法

    LINQでjoinを使えば良いです。

    (参考)
    Inner join of DataTables in C#
    https://stackoverflow.com/questions/665754/inner-join-of-datatables-in-c-sharp


    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!


    2018年5月24日 0:18
  • おおよそ問題ないのですが、T2.Name2が一意でない場合にINNER JOINでは結果が重複します(各行が連結されるため)。質問文にも一意かどうかは言及されていませんでした。

    そのため私の回答では IN 句を紹介しました。

    2018年5月24日 0:59
  • > .NET Framework でデータテーブルを扱おうとしているんですが

    T1, T2, T3 とも .NET の System.Data 名前空間 System.Data.dll 内 の DataTable クラスのオブジェクトなのですか? 「データテーブル」という言葉からは DataTable を想像するのですが。

    DataTable クラス
    https://msdn.microsoft.com/ja-jp/library/system.data.datatable(v=vs.110).aspx

    以下は、T1, T2, T3 とも DataTable と理解してレスします。

    そうではなくて、T1, T2, T3 が SQL Server 等のデータベースのテーブルであれば、データベースは何かとそのバージョン・エディションを書いてください。

    > これを SQL コマンドでやる方法 (と、もしご存知なら .NET Framework で実装する方法) 

    DataTable であれば SELECT ... FROM ... というクエリではできませんので、Linq を使って join した結果を得るのが妥当だと思います。

    Microsoft が提供しているサンプルデータベース Northwind の Products テーブル、Categories テーブルから DataTable を作成し、それらを Linq で CategoryID フィールドが等しいという条件で内部結合して結果を取得するサンプルをアップしておきます。

    皆さんが懸念されている重複の排除のためのコードも一行追加しておきました。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace ConsoleAppDataTableJoinByLinq
    {
        class Program
        {
            static void Main(string[] args)
            {
                // 2 つの DataTable を作成。
                DataTable products = new DataTable();    // T1 に該当
                DataTable categories = new DataTable();  // T2 に該当
    
                string selectProducts = "SELECT ProductID, ProductName, CategoryID FROM Products";
                string selectCategories = "SELECT CategoryID, CategoryName FROM Categories";
    
                string connString = @"接続文字列";
    
                using (SqlConnection conn = new SqlConnection(connString))
                {
                    SqlDataAdapter adapter = new SqlDataAdapter();
                    SqlCommand cmd = new SqlCommand();
                    cmd.Connection = conn;
                    cmd.CommandText = selectProducts;
                    adapter.SelectCommand = cmd;
                    adapter.Fill(products);
                    adapter.SelectCommand.CommandText = selectCategories;
                    adapter.Fill(categories);
                }
    
                // IEnumerable<匿名型> として取得
                var list = from p in products.AsEnumerable()
                           join c in categories.AsEnumerable()
                           on p.Field<int?>("CategoryID") equals c.Field<int>("CategoryID")
                           select new
                           {
                               ProductID = p.Field<int>("ProductID"),
                               ProductName = p.Field<string>("ProductName"),
                               CategoryName = c.Field<string>("CategoryName")
                           };
    
                // 重複排除。匿名型なのでこれだけで可
                list = list.Distinct();
    
                foreach (var item in list)
                {
                    Console.WriteLine("ProductID: {0}, ProductName: {1}, CategoryName: {2}",
                        item.ProductID, item.ProductName, item.CategoryName);
                }
    
                // 結果は:
                // ProductID: 1, ProductName: Chai, CategoryName: Beverages
                // ProductID: 2, ProductName: Chang, CategoryName: Beverages
                // ProductID: 3, ProductName: Aniseed Syrup, CategoryName: Condiments
                // ・・・中略・・・
                // ProductID: 77, ProductName: Original Frankfurter grune Sose, CategoryName: Condiments
    
                // 結果を DataTable に格納(T3 に該当)
                DataTable dt = new DataTable();
                dt.Columns.Add(new DataColumn("ProductID", typeof(int)));
                dt.Columns.Add(new DataColumn("ProductName", typeof(string)));
                dt.Columns.Add(new DataColumn("CategoryName", typeof(string)));
    
                foreach (var item in list)
                {
                    DataRow row = dt.NewRow();
                    row["ProductID"] = item.ProductID;
                    row["ProductName"] = item.ProductName;
                    row["CategoryName"] = item.CategoryName;
                    dt.Rows.Add(row);
                }
            }
        }
    }

    2018年5月24日 2:11
  • おおよそ問題ないのですが、T2.Name2が一意でない場合にINNER JOINでは結果が重複します(各行が連結されるため)。質問文にも一意かどうかは言及されていませんでした。

    確かにその通りですね。気づいていませんでした。
    その場合、inner joinやLINQだとdistinctを付ける必要がありますね。
    ただ、実際にはおっしゃる通り、T1とT2で重複があるのかないのか、あった場合に結果をどう取得したいのかによって手段が変わってきますね。

    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!

    2018年5月24日 1:44
  • しつこくてすみません。

    T1に重複があった場合に、DISTINCTの付け方によっては重複が間引かれてしまいます。

    重複の有無、結果をどうしたいのか、は質問者さんが明確にしていただくのがベストですが、言及されていない場合はAS IS; 現状維持がベストかなと。

    2018年5月24日 1:59
  • しつこくてすみません。

    T1に重複があった場合に、DISTINCTの付け方によっては重複が間引かれてしまいます。

    重複の有無、結果をどうしたいのか、は質問者さんが明確にしていただくのがベストですが、言及されていない場合はAS IS; 現状維持がベストかなと。

    いえいえ、質問者さんがつまずかないようにこのような情報は大事だと思います。
    現状、IN句を使うのが一番素直な方法だと私も思います。

    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!

    2018年5月24日 2:30

すべての返信

  • 何のひねりもありませんが…
    SELECT [Name], [Value]
    FROM T1
    WHERE [Name] IN (SELECT [Name2] FROM T2);

    2018年5月23日 12:48
  • 普通にinner joinで良いのでは?

    select Name, Value
    from T1
    inner join T2 on T1.Name = T2.Name

    (追記)
    と、もしご存知なら .NET Framework で実装する方法

    LINQでjoinを使えば良いです。

    (参考)
    Inner join of DataTables in C#
    https://stackoverflow.com/questions/665754/inner-join-of-datatables-in-c-sharp


    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!


    2018年5月24日 0:18
  • おおよそ問題ないのですが、T2.Name2が一意でない場合にINNER JOINでは結果が重複します(各行が連結されるため)。質問文にも一意かどうかは言及されていませんでした。

    そのため私の回答では IN 句を紹介しました。

    2018年5月24日 0:59
  • おおよそ問題ないのですが、T2.Name2が一意でない場合にINNER JOINでは結果が重複します(各行が連結されるため)。質問文にも一意かどうかは言及されていませんでした。

    確かにその通りですね。気づいていませんでした。
    その場合、inner joinやLINQだとdistinctを付ける必要がありますね。
    ただ、実際にはおっしゃる通り、T1とT2で重複があるのかないのか、あった場合に結果をどう取得したいのかによって手段が変わってきますね。

    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!

    2018年5月24日 1:44
  • しつこくてすみません。

    T1に重複があった場合に、DISTINCTの付け方によっては重複が間引かれてしまいます。

    重複の有無、結果をどうしたいのか、は質問者さんが明確にしていただくのがベストですが、言及されていない場合はAS IS; 現状維持がベストかなと。

    2018年5月24日 1:59
  • > .NET Framework でデータテーブルを扱おうとしているんですが

    T1, T2, T3 とも .NET の System.Data 名前空間 System.Data.dll 内 の DataTable クラスのオブジェクトなのですか? 「データテーブル」という言葉からは DataTable を想像するのですが。

    DataTable クラス
    https://msdn.microsoft.com/ja-jp/library/system.data.datatable(v=vs.110).aspx

    以下は、T1, T2, T3 とも DataTable と理解してレスします。

    そうではなくて、T1, T2, T3 が SQL Server 等のデータベースのテーブルであれば、データベースは何かとそのバージョン・エディションを書いてください。

    > これを SQL コマンドでやる方法 (と、もしご存知なら .NET Framework で実装する方法) 

    DataTable であれば SELECT ... FROM ... というクエリではできませんので、Linq を使って join した結果を得るのが妥当だと思います。

    Microsoft が提供しているサンプルデータベース Northwind の Products テーブル、Categories テーブルから DataTable を作成し、それらを Linq で CategoryID フィールドが等しいという条件で内部結合して結果を取得するサンプルをアップしておきます。

    皆さんが懸念されている重複の排除のためのコードも一行追加しておきました。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace ConsoleAppDataTableJoinByLinq
    {
        class Program
        {
            static void Main(string[] args)
            {
                // 2 つの DataTable を作成。
                DataTable products = new DataTable();    // T1 に該当
                DataTable categories = new DataTable();  // T2 に該当
    
                string selectProducts = "SELECT ProductID, ProductName, CategoryID FROM Products";
                string selectCategories = "SELECT CategoryID, CategoryName FROM Categories";
    
                string connString = @"接続文字列";
    
                using (SqlConnection conn = new SqlConnection(connString))
                {
                    SqlDataAdapter adapter = new SqlDataAdapter();
                    SqlCommand cmd = new SqlCommand();
                    cmd.Connection = conn;
                    cmd.CommandText = selectProducts;
                    adapter.SelectCommand = cmd;
                    adapter.Fill(products);
                    adapter.SelectCommand.CommandText = selectCategories;
                    adapter.Fill(categories);
                }
    
                // IEnumerable<匿名型> として取得
                var list = from p in products.AsEnumerable()
                           join c in categories.AsEnumerable()
                           on p.Field<int?>("CategoryID") equals c.Field<int>("CategoryID")
                           select new
                           {
                               ProductID = p.Field<int>("ProductID"),
                               ProductName = p.Field<string>("ProductName"),
                               CategoryName = c.Field<string>("CategoryName")
                           };
    
                // 重複排除。匿名型なのでこれだけで可
                list = list.Distinct();
    
                foreach (var item in list)
                {
                    Console.WriteLine("ProductID: {0}, ProductName: {1}, CategoryName: {2}",
                        item.ProductID, item.ProductName, item.CategoryName);
                }
    
                // 結果は:
                // ProductID: 1, ProductName: Chai, CategoryName: Beverages
                // ProductID: 2, ProductName: Chang, CategoryName: Beverages
                // ProductID: 3, ProductName: Aniseed Syrup, CategoryName: Condiments
                // ・・・中略・・・
                // ProductID: 77, ProductName: Original Frankfurter grune Sose, CategoryName: Condiments
    
                // 結果を DataTable に格納(T3 に該当)
                DataTable dt = new DataTable();
                dt.Columns.Add(new DataColumn("ProductID", typeof(int)));
                dt.Columns.Add(new DataColumn("ProductName", typeof(string)));
                dt.Columns.Add(new DataColumn("CategoryName", typeof(string)));
    
                foreach (var item in list)
                {
                    DataRow row = dt.NewRow();
                    row["ProductID"] = item.ProductID;
                    row["ProductName"] = item.ProductName;
                    row["CategoryName"] = item.CategoryName;
                    dt.Rows.Add(row);
                }
            }
        }
    }

    2018年5月24日 2:11
  • しつこくてすみません。

    T1に重複があった場合に、DISTINCTの付け方によっては重複が間引かれてしまいます。

    重複の有無、結果をどうしたいのか、は質問者さんが明確にしていただくのがベストですが、言及されていない場合はAS IS; 現状維持がベストかなと。

    いえいえ、質問者さんがつまずかないようにこのような情報は大事だと思います。
    現状、IN句を使うのが一番素直な方法だと私も思います。

    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!

    2018年5月24日 2:30
  • うわわ、すみません、アラートが来なかったので てっきり回答ゼロだと思って 忘れてました;;

    元々 .Net の DataTable を JavaScript でコーディングしていたので、複雑な Linq が使えずに困ってした質問でした。

    結局、クエリでなく コードでゴリゴリ抽出して間に合わせていました。

    頂いた回答 (わざわざ長いコードまで頂いて恐縮です;) を参考にして、スマートに作り直してみようと思いまず。

    ありがとうございました。

    2020年12月13日 11:54