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

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

新規登録して質問してみよう
ただいま回答率
85.38%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

26381閲覧

LINQを使用してDataTableを結合したい

r-user

総合スコア12

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

1グッド

0クリップ

投稿2017/10/30 02:31

編集2017/10/31 01:12

表題通り、2つのDataTableを結合したいのです。

DataTable1に列ABCが含まれており、DataTable2に列ABDが含まれていたとしたら、結合し列ABCDのDataTableを作成したいのです。
列A,Bの値が一致していたら結合させたいのですが、下記のコードだとDataTable1そのままが返ってきます。
LinQのselectの部分が間違っているのではないかと考え、列1つ1つ匿名型で指定する方法も試そうとしましたが、列が多くできれば完結に記載したいのですが調べても答えを見つけられませんでした。

拙い文章ではありますが、何卒ご回答の程よろしくお願いします。
環境
OS:windows7
visualstudio:2017

C#

1 private DataTable dtJoiner(DataTable targetDt, DataTable otherDt) 2 { 3 var rows = from targetDtRow in targetDt.AsEnumerable() 4 join otherDtRow in otherDt.AsEnumerable() 5 on new { a= targetDtRow.Field<string>("A"), b= targetDtRow.Field<string>("B") } equals new { a= otherDtRow.Field<Int32>("A").ToString(), b= otherDtRow.Field<Int32>("B").ToString() } 6 select targetDtRow; 7 8 return rows.CopyToDataTable(); 9 }

#追記
テーブル1   テーブル2
A列 B列 C列 A列 B列 D列
1 2 a    1 2 d
1 2 b
1 2 c
のDataTableがあった場合、理想としては結合後のテーブルは
テーブル 3
A列 B列 C列 D列
1 2 a d
1 2 b d
1 2 c d
なのですが実際は
テーブル 3
A列 B列 C列 D列
1 2 a d
1 2 a d
1 2 a d
になってしまいます。
重複させないようにしたいのですが不可能でしょうか?

bochan2👍を押しています

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2017/10/30 03:16

もう少し具体的に、どういうことを期待してそのコードを書いたが、実行した結果こうなったというところを書いていただけませんか?(特に結果) また、自分の開発環境(OS, .NET, Visual Studio のバージョンなど)も書いてください。質問するときのイロハのイです。
r-user

2017/10/30 04:00

大変失礼いたしました。質問の仕方が雑で申し訳ありませんでした。
guest

回答2

0

ベストアンサー

こんなのは如何でしょう?

C#

1 private DataTable dtJoiner(DataTable targetDt, DataTable otherDt) 2 { 3 var response = new DataTable(); 4 5 new List<string> { "A", "B", "C", "D" } 6 .ForEach(c => response.Columns.Add(c, typeof(string))); 7 8 var rows = targetDt.AsEnumerable() 9 .Join(otherDt.AsEnumerable(), 10 t => new { a = t.Field<string>("A"), b = t.Field<string>("B") }, 11 o => new { a = o.Field<string>("A"), b = o.Field<string>("B") }, 12 (t, o) => 13 { 14 var row = response.NewRow(); 15 row.ItemArray = new[] { t.Field<string>("A"), t.Field<string>("B"), t.Field<string>("C"), o.Field<string>("D") }; 16 return row; 17 }); 18 19 foreach(var row in rows) 20 { 21 response.Rows.Add(row); 22 } 23 24 return response; 25 }

PS. DataTable は過去の遺物で、重たく扱いづらい代物です。
また最近の主流であるLINQとは相性が悪いです。

-- リクエストにより追記 --
一度匿名型にして Distinct 拡張メソッドで重複を取り除いています。

C#

1var rows = targetDt.AsEnumerable() 2 .Join(otherDt.AsEnumerable(), 3 t => new { a = t.Field<string>("A"), b = t.Field<string>("B") }, 4 o => new { a = o.Field<string>("A"), b = o.Field<string>("B") }, 5 (t, o) => new 6 { 7 a = t.Field<string>("A"), 8 b = t.Field<string>("B"), 9 c = t.Field<string>("C"), 10 d = o.Field<string>("D") 11 }) 12 .Distinct() 13 .Select(j => 14 { 15 var row = response.NewRow(); 16 row.ItemArray = new[] { j.a, j.b, j.c, j.d }; 17 return row; 18 });

投稿2017/10/30 04:39

編集2017/10/31 03:08
hihijiji

総合スコア4152

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

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

toro1

2017/10/30 06:52

datatable捨てましょうと言っているわけではないでしょうが、 DataTableやDataSetは、Linq以前からの機能で、いろいろな機能を備えているので、重たいですし、いろいろな機能があるがゆえに扱いづらかったりしますが、datagridviewとの相性はLinqと比べてよかったりします。(例えば、datagridviewの列ヘッダをクリックでソートするなど) つまり、使いどころかと思います。
hihijiji

2017/10/30 08:15

使ったほうが能率が上がるケースにまで言及するつもりはないのですが、初心者を中心に何故か DataTable の質問が多いような気がします。 匿名型や ValueTuple など便利で解り易い機能で入門した人が DataTable の扱いに戸惑ってるのならいいのです。 もしや、古い参考書や資料を見て DataTable は入門に適したデータ構造であると誤解しているのでは?と杞憂しての書き込みです。
r-user

2017/10/31 00:20

ご回答ありがとうございます。 期待していた通りの動きでした。 ありがとうございます。
r-user

2017/10/31 01:02

1つお聞きしたいのですが、条件に一致したデータが複数存在した場合、一致した回数分、最初に一致したデータが配列に格納されるのですが重複するデータはスキップすることは可能でしょうか。追記に図を記載いたしますのでご参照いただければと思います。
r-user

2017/10/31 04:45

追記していただきありがとうございます。 Linqについてどういった動きをしているのか把握できていないで、コードをまねるだけではなく、自分で一度きちんと調べたいと思います。丁寧に対応していただきありがとうございます。
r-user

2017/10/31 04:58

ありがとうございます。
guest

0

質問者さんの作った DataTable の内容が不明ですので、アップされたコードをどう直せばよいかはなかなか答えにくいです。

なので、Microsoft が提供するサンプルデータベース Northwind の Orders, Customers テーブルから以下のクエリを使って DataTable を作り、それを Linq を使って CustomerID が一致する条件で Inner Join し、さらに ShipCity と City が一致する条件で抽出するサンプルを書きました。

SELECT OrderID, CustomerID, ShipCity FROM Orders SELECT CustomerID, CompanyName, City FROM Customers

コードは以下の通りです。不明な点があれば聞いてください。また、これが質問者さんのやりたいことと違う場合は、どこがどのように違うかを連絡ください。

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 orders = new DataTable(); DataTable customers = new DataTable(); string selectOrders = "SELECT OrderID, CustomerID, ShipCity FROM Orders"; string selectCustomers = "SELECT CustomerID, CompanyName, City FROM Customers"; string connStr = @"Data Source=(local)\sqlexpress;Initial Catalog=NORTHWIND;Integrated Security=True"; using (SqlConnection conn = new SqlConnection(connStr)) { SqlDataAdapter adapter = new SqlDataAdapter(); SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.CommandText = selectOrders; adapter.SelectCommand = cmd; adapter.Fill(orders); adapter.SelectCommand.CommandText = selectCustomers; adapter.Fill(customers); } var result = from o in orders.AsEnumerable() join c in customers.AsEnumerable() on o.Field<string>("CustomerID") equals c.Field<string>("CustomerID") where o.Field<string>("ShipCity") == c.Field<string>("City") select new { OrderID = o.Field<int>("OrderID"), CompanyName = c.Field<string>("CompanyName"), ShipCity = o.Field<string>("ShipCity") }; foreach (var item in result) { Console.WriteLine("OrderID: {0}, CompanyName: {1}, ShipCity: {2}", item.OrderID, item.CompanyName, item.ShipCity); } // 結果は: // OrderID: 10248, CompanyName: Vins et alcools Chevalier, ShipCity: Reims // OrderID: 10250, CompanyName: Hanari Carnes, ShipCity: Rio de Janeiro // OrderID: 10251, CompanyName: Victuailles en stock, ShipCity: Lyon // ・・・中略・・・ // OrderID: 11077, CompanyName: Rattlesnake Canyon Grocery, ShipCity: Albuquerque // 結果を DataTable に格納 DataTable table = new DataTable(); table.Columns.Add(new DataColumn("OrderID", typeof(int))); table.Columns.Add(new DataColumn("CompanyName", typeof(string))); table.Columns.Add(new DataColumn("ShipCity", typeof(string))); foreach (var item in result) { DataRow row = table.NewRow(); row["OrderID"] = item.OrderID; row["CompanyName"] = item.CompanyName; row["ShipCity"] = item.ShipCity; table.Rows.Add(row); } } } }

【追記】

コメント欄の追加質問の件です。

条件に一致したデータが複数存在した場合は既に配列に存在した場合はスキップしたい

IEnumerable<T> で T が匿名型の場合(上のコードで言うと result がそれ)は、単純に result に Distinct() メソッドを適用すれば期待通り重複のない結果が得られます。具体的には例えば、以下のようにしてください。

var result = from o in orders.AsEnumerable() join c in customers.AsEnumerable() on o.Field<string>("CustomerID") equals c.Field<string>("CustomerID") where o.Field<string>("ShipCity") == c.Field<string>("City") select new { OrderID = o.Field<int>("OrderID"), CompanyName = c.Field<string>("CompanyName"), ShipCity = o.Field<string>("ShipCity") }; result = result.Distinct(); // この行を追加

なお、T にカスタムデータ型を使った場合はそう簡単には行きません。詳しくは以下の記事を見てください。

匿名型と Distinct メソッド
http://surferonwww.info/BlogEngine/post/2015/12/08/anonymous-type-and-distinct-method.aspx

投稿2017/10/30 06:24

編集2017/10/31 03:42
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

r-user

2017/10/31 01:01

ご回答ありがとうございます。参考にさせていただきます。 1つお聞きしたいのですが、条件に一致したデータが複数存在した場合は既に配列に存在した場合はスキップしたいのですが、where文で可能でしょうか。
退会済みユーザー

退会済みユーザー

2017/10/31 03:34

> 条件に一致したデータが複数存在した場合は既に配列に存在した場合はスキップしたい 回答欄に追記しておきます。
r-user

2017/10/31 04:43

詳しいご回答ありがとうございます。Distinctについては調べればわかることでしたね。 今後自分で調べてそのうえで質問するよう気を付けます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.38%

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

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

質問する

関連した質問