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

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

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

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

Q&A

解決済

3回答

1498閲覧

LINQ to DatasetによるData Tableからのデータ抽出

sicnweouif

総合スコア12

C#

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

0グッド

0クリップ

投稿2018/10/25 08:09

編集2018/10/26 03:16

前提・実現したいこと

C#でユーザの入力をもとにData Tableを検索し、抽出データを表示するシステムの実装を行っています。Data TableにはXMLファイルのデータを読み込んでいます。そこで、LINQ to DatasetによるData Tableからのデータ抽出を行おうと考えてます。

DataTableにはDateTime型の"日時"列があります.その"日時"列に注目して,ある日付の行データ,あるいはそこから遡った1週間分の行データを抽出したいと考えております.具体的に言えば,「2018/10/25から1週間前までのデータを抽出」といったような感じです.また検索条件の日付は,変数として可変に扱うことができるようにしたいです.
DateTime型の比較により,一定幅の連続した行データを抽出するにはwhere句でどのように検索条件を指定すればよいのでしょうか.

稚拙な質問ですが,どなたかお教え頂ければ幸いです。

XMLファイル

<?xml version="1.0" encoding="utf-8"?> <DocumentElement> <data> <日時>2018/10/23 9:50</日時> <場所>会議室</場所> <メモ>ミーティング</メモ> </data> <data> <日時>2018/09/23 18:30</日時> <場所>居酒屋</場所> <メモ>飲み会</メモ> </data> <data> <日時>2018/09/22 11:50</日時> <場所>温泉</場所> <メモ>旅行</メモ> </data> <data> <日時>2018/09/21 20:00</日時> <場所>居酒屋</場所> <メモ>飲み会</メモ> </data> </DocumentElement>

該当のソースコード

C#

1namespace Program 2{ 3 public partial class Display : Form 4 { 5 private string DataFileName = System.IO.Path.Combine(Application.StartupPath, "XMLFile1.xml"); 6 private DataSet Ds = new DataSet(); 7 private DataTable Dl = new DataTable(); 8 9 10 public SearchDisplay3() 11 { 12 InitializeComponent(); 13 Dl.Columns.Add("日時", typeof(DateTime)); 14 } 15 16 private void Button_Click(object sender, EventArgs e) 17 { 18 Ds.Tables.Clear(); 19 Ds.ReadXml(DataFileName); 20 Dl = Ds.Tables[0]; 21 } 22 } 23} 24

補足情報

修正が遅れてしまい,申し訳ございません.
少し強引なやり方かもしれませんが,XMLファイルを読み込む前に,DlにあらかじめDateTime型の列を追加しています.
XMLファイルの"日時"列の要素を2018年10月23日 15:00などにかえるとDlに読み込む際に
「文字列は有効なDate Timeではありませんでした.」
というエラーが出るので,DateTime型のデータしか受け付けない仕様になっているはずだと解釈しておりました.

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

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

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

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

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

Zuishin

2018/10/25 08:29

実際に使える XML のサンプルとそれを DataTable に読み込んでいる部分のコードを出してください。
Zuishin

2018/10/25 08:30

実際に使える XML のサンプルとそれを DataTable に読み込んでいる部分のコードを出してください。
Zuishin

2018/10/25 08:30

実際に使える XML のサンプルとそれを DataTable に読み込んでいる部分のコードを出してください。
退会済みユーザー

退会済みユーザー

2018/10/25 11:40 編集

> DataTableにはDateTime型の"日時"列があります ← それは間違いなく DateTime 型なのですか? 回答されている方はそれに疑いを持たれてないようですが、失礼ながら、自分はそれがかなり疑わしい(実は String 型ではないか)と思っているのですが・・・ Zuishin さんのコメントに答えてもらえるとそのあたりが分かると思います。
Zuishin

2018/10/26 03:41 編集

ほら、思った通り間違ってる。ざっと見ただけでも、スキーマが無いのと DataTable を捨ててるのがわかります。詳しく調べればもっとあるでしょう。
sicnweouif

2018/10/26 03:47

Data Tableを捨てているというのはどういうことでしょうか.
Zuishin

2018/10/26 04:40

SurferOnWww さんの解説の通りです。Data Table ではなく DataTable です。ここで聞かれたことは無視せず素直に答えましょう。必要だから聞いているんです。
Zuishin

2018/10/26 04:41

必要なのは私にとってではなく、回答者が回答するにあたって必要ということです。
Zuishin

2018/10/26 04:43

またそれは、あなたが自分で解決できない以上、問題解決に必要ということです。
Zuishin

2018/10/27 02:17

わかったら返事。わからなかったらお気の毒様。
guest

回答3

0

ベストアンサー

少し強引なやり方かもしれませんが,XMLファイルを読み込む前に,DlにあらかじめDateTime型の列を追加しています.

それは意味がないです。xml ファイルにスキーマを付与して型を指定してから Ds.ReadXml(DataFileName); で読むようにしてください。

Data Tableを捨てているというのはどういうことでしょうか.

private DataTable Dl = new DataTable(); の Dl と Dl = Ds.Tables[0]; の Dl が指すオブジェクトは別物ですよ。

xml ファイルにスキーマがない場合、Ds.ReadXml(DataFileName); で xml ファイルを読んで DataSet/DataTable を作ると、DataTable の全部の列が String 型になるはずです。

スキーマというのがどういうものかと言うと、例えば以下の xml ファイルで <xs:schema ...>...</xs:schema> の部分がそれです。

<?xml version="1.0" standalone="yes"?> <NewDataSet> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="Table1"> <xs:complexType> <xs:sequence> <xs:element name="ID" type="xs:int" minOccurs="0" /> <xs:element name="Name" type="xs:string" minOccurs="0" /> <xs:element name="Price" type="xs:decimal" minOccurs="0" /> <xs:element name="Discontinued" type="xs:boolean" minOccurs="0" /> <xs:element name="Date" type="xs:dateTime" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> <Table1> <ID>0</ID> <Name>Product Name_0</Name> <Price>123000</Price> <Discontinued>true</Discontinued> <Date>2018-10-26T10:18:11.9032337+09:00</Date> </Table1> <Table1> <ID>1</ID> <Name>Product Name_1</Name> <Price>246000</Price> <Discontinued>false</Discontinued> <Date>2018-10-27T10:18:11.9042409+09:00</Date> </Table1>  ・・・中略・・・ </NewDataSet>

kiichi54321 さん、runny_nose さんの回答は DataTable の当該列が DateTime 型であるという前提だと思いますが、そうだとすると DataTable を直さないと話が違ってくると思います。

【追記】

下の私の 2018/10/27 11:23 のコメントで「後で検証して回答欄に追記しておきます」と書きましたが、それを以下に書きます。

上にアップしたスキーマ付きの xml ファイルを DataSet.ReadXml メソッドで DataSet/ DataTable に読み込めば Date 列は DateTime 型になります。

それから前回のスレッド https://teratail.com/questions/153141 の応用で DataRow[] 型のオブジェクトを取得できます。

具体的には、以下のようにすれば、

DataSet dataset1 = new DataSet(); dataset1.ReadXml("xml ファイルのパス"); DataRow[] datarows = (from row in dataset1.Tables[0].AsEnumerable() let date = row.Field<DateTime>("Date") where date <= DateTime.Now && date >= DateTime.Now.AddDays(-7) select row).ToArray(); foreach (DataRow row in datarows) { Console.WriteLine($"ID: {row[0]}, Name: {row[1]}, Price: {row[2]}, Discontinued: {row[3]}, Date: {row[4]}"); }

結果は以下のようになります:

ID: 9, Name: Product Name_9, Price: 1230000, Discontinued: False, Date: 2018/10/21 11:10:33 ID: 10, Name: Product Name_10, Price: 1353000, Discontinued: True, Date: 2018/10/22 11:10:33 ID: 11, Name: Product Name_11, Price: 1476000, Discontinued: False, Date: 2018/10/23 11:10:33 ID: 12, Name: Product Name_12, Price: 1599000, Discontinued: True, Date: 2018/10/24 11:10:33 ID: 13, Name: Product Name_13, Price: 1722000, Discontinued: False, Date: 2018/10/25 11:10:33 ID: 14, Name: Product Name_14, Price: 1845000, Discontinued: True, Date: 2018/10/26 11:10:33

【追記2】

下の私の 2018/10/27 15:22 のコメントで「具体的にどのようにするかは後で回答欄に追記しておきます」と書きましたが、それを以下に書きます。

コードを読めばわかると思うのでコードだけ。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string dir = @"どこかの適当なフォルダ"; DataSet ds = CreateDataSource(); string file = "withschema.xml"; ds.WriteXml(dir + file, XmlWriteMode.WriteSchema); file = "WithoutSchema.xml"; ds.WriteXml(dir + file, XmlWriteMode.IgnoreSchema); DataSet dataset1 = new DataSet(); DataSet dataset2 = new DataSet(); dataset1.ReadXml(dir + "WithSchema.xml"); dataset2.ReadXml(dir + "WithoutSchema.xml"); Type type1 = dataset1.Tables[0].Columns["Date"].DataType; Type type2 = dataset2.Tables[0].Columns["Date"].DataType; Console.WriteLine($"Type: {type1}"); Console.WriteLine($"Type: {type2}"); // 結果は: // Type: System.DateTime // Type: System.String DataRow[] datarows = (from row in dataset1.Tables[0].AsEnumerable() let date = row.Field<DateTime>("Date") where date <= DateTime.Now && date >= DateTime.Now.AddDays(-7) select row).ToArray(); foreach (DataRow row in datarows) { Console.WriteLine($"ID: {row[0]}, Name: {row[1]}, Price: {row[2]}, Discontinued: {row[3]}, Date: {row[4]}"); } // 結果は: // ID: 9, Name: Product Name_9, Price: 1230000, Discontinued: False, Date: 2018 / 10 / 21 15:27:08 // ID: 10, Name: Product Name_10, Price: 1353000, Discontinued: True, Date: 2018 / 10 / 22 15:27:08 // ID: 11, Name: Product Name_11, Price: 1476000, Discontinued: False, Date: 2018 / 10 / 23 15:27:08 // ID: 12, Name: Product Name_12, Price: 1599000, Discontinued: True, Date: 2018 / 10 / 24 15:27:08 // ID: 13, Name: Product Name_13, Price: 1722000, Discontinued: False, Date: 2018 / 10 / 25 15:27:08 // ID: 14, Name: Product Name_14, Price: 1845000, Discontinued: True, Date: 2018 / 10 / 26 15:27:08 } // データソース用の DataSet を作成 protected static DataSet CreateDataSource() { DataTable dt = new DataTable(); DataRow dr; dt.Columns.Add(new DataColumn("ID", typeof(Int32))); dt.Columns.Add(new DataColumn("Name", typeof(string))); dt.Columns.Add(new DataColumn("Price", typeof(decimal))); dt.Columns.Add(new DataColumn("Discontinued", typeof(bool))); dt.Columns.Add(new DataColumn("Date", typeof(DateTime))); for (int i = 0; i < 15; i++) { dr = dt.NewRow(); dr["ID"] = i; dr["Name"] = "Product Name_" + i.ToString(); dr["Price"] = 123000 * (i + 1); dr["Discontinued"] = (i % 2 == 0) ? true : false; dr["Date"] = DateTime.Now.AddDays(i - 15); dt.Rows.Add(dr); } DataSet ds = new DataSet(); ds.Tables.Add(dt); return ds; } } }

投稿2018/10/26 04:28

編集2018/10/27 09:01
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

sicnweouif

2018/10/27 01:05

スキーマの必要性について理解が足りていませんでした。ご指摘ありがとうございます。 スキーマは使用するXMLファイルとは別のファイルとして用意しておかなければらないと思っていましたが、上記の例のように、読み込みたいXMLファイルの冒頭部分に組み込んでおくことも可能なのですね。
退会済みユーザー

退会済みユーザー

2018/10/27 02:23

DataTable の当該列の型がちゃんと DateTime 型になっていれば、前のスレッド https://teratail.com/questions/153141 の応用でできるはずです。(たぶんやってみたができなかったからこのスレッドの質問になったのではないかと想像してます) 後で検証して回答欄に追記しておきます。
sicnweouif

2018/10/27 05:38

追記ありがとうございます。 自作のXMLファイルにスキーマを追加して、頂いた例を参考にしながら試してみます。
退会済みユーザー

退会済みユーザー

2018/10/27 06:22

> 自作のXMLファイルにスキーマを追加して、 これからスキーマについて勉強するというのも良いかもしれませんが、とりあえず、各列に型を定義した DataTable / DataSet を作成し、それから DataSet.WriteXml("ファイルパス", XmlWriteMode.WriteSchema); で xml ファイルを作成し、それを参考にしてはいかがですか? 実は、上の回答に書いた xml ファイルもそのように作成しています。具体的にどのようにするかは後で回答欄に追記しておきます。 スキーマの勉強についてはその後でも良いと思います。
sicnweouif

2018/10/27 13:42

返信遅くなってしまいすみません。参考にさせていただきます。 私事で大変申し訳ないのですが、私用のため作業が明日出来ないため、明後日以降改めて連絡させていただければと思います。
sicnweouif

2018/10/29 06:30

返信が遅くなってしまい申し訳ございません. 追記の方でいただいたコードを参考にさせていただき,スキーマ付きのXMLファイルを作成することができました.ありがとうございます.
退会済みユーザー

退会済みユーザー

2018/10/29 12:01

解決したなら放置しておかないでクローズしてください。
guest

0

.Where(n=>n.date < date && n.date > date.AddDays(-7))

こんな感じ。

XMLなら、オブジェクトにシリアライズすればいいだけでは?って感じだけど。
なんで、Datasetをつかうの?とか思うけど。
https://improveandrepeat.com/2017/08/paste-xml-as-class-in-visual-studio-2017/

投稿2018/10/25 08:43

kiichi54321

総合スコア1984

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

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

sicnweouif

2018/10/26 03:17

回答ありがとうございます. オブジェクトへのシリアライズについても検討させていただきます. リンクも貼っていただきありがとうございます.
guest

0

こういうことでしょうか?

csharp

1DateTime start = new DateTime(2018, 10, 25); 2DateTime end = start.AddDays(7); 3 4var rows = dt.AsEnumerable().Where(row => row["日時"] is DateTime 5 && start <= (DateTime)row["日時"] 6 && (DateTime)row["日時"] <= end);

抽出条件の部分は下記のように拡張メソッドを作るとよいかもしれません。

csharp

1public static bool Between<T>(this T value, T from, T to) where T : IComparable<T> 2{ 3 return value.CompareTo(from) >= 0 && value.CompareTo(to) <= 0; 4} 5 6 7DateTime start = new DateTime(2018, 10, 25); 8DateTime end = start.AddDays(7); 9 10var rows = dt.AsEnumerable().Where(row => ((DateTime)row["日時"]).Between(start, end)); 11

実際のソースを見た後での追記

DataTableである必要があるのでしょうか?
やはりオブジェクトにデシリアライズするほうが、扱いやすく拡張もしやすいように思われます。
例えば、↓こんなクラスをつくり・・・

csharp

1[XmlType("data")] 2public class Plan 3{ 4 [XmlElement("日時")] 5 public DateTime ScheduledDate { get; set; } 6 [XmlElement("場所")] 7 public string Place { get; set; } 8 [XmlElement("メモ")] 9 public string Note { get; set; } 10}

↓こんなユーティリティクラスを用意しといて・・・

csharp

1public static class XmlUtility 2{ 3 public static T ConvertToObject<T>(string path, string xmlRoot = null) where T : class 4 { 5 T deserializedObject = default(T); 6 7 if (!File.Exists(path)) 8 { 9 return deserializedObject; 10 } 11 12 XmlSerializer serializer = string.IsNullOrWhiteSpace(xmlRoot) ? new XmlSerializer(typeof(T)) : new XmlSerializer(typeof(T), new XmlRootAttribute(xmlRoot)); 13 using (var reader = new StreamReader(path)) 14 { 15 deserializedObject = serializer.Deserialize(reader) as T; 16 } 17 18 return deserializedObject; 19 } 20}

↓こんな感じにする

csharp

1public partial class Display : Form 2{ 3 private string DataFileName = System.IO.Path.Combine(Application.StartupPath, "XMLFile1.xml"); 4 5 public Display() 6 { 7 InitializeComponent(); 8 } 9 10 private void Button_Click(object sender, EventArgs e) 11 { 12 // デシリアライズ 13 List<Plan> plans = XmlUtility.ConvertToObject<List<Plan>>(DataFileName, "DocumentElement"); 14 if (plans != null) 15 { 16 // 現在から1週間分に絞り込み 17 DateTime start = DateTime.Now; 18 DateTime end = start.AddDays(7); 19 var thisWeekPlans = plans.Where(p => p.ScheduledDate.Between(start, end)).ToList(); 20 // DataGridViewに表示したいと想像 21 dataGridView.DataSource = thisWeekPlans; 22 } 23 } 24}

また、XMLの日時の書式は"YYYY-MM-DDThh:mm:ss"である必要があります。

投稿2018/10/25 08:39

編集2018/10/30 09:01
runny_nose

総合スコア280

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

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

sicnweouif

2018/10/26 04:15

回答ありがとうございます. 試してみます.
sicnweouif

2018/10/27 00:57

ご丁寧にコードも記載いただきありがとうございます。 コードを解析して自分なりに実装してみて、改めて連絡させていただきます。
sicnweouif

2018/10/30 02:50

返信が遅れてしまい申し訳ございません. 無事,オブジェクトにデシリアライズすることが出来ました. ユーティリティクラスのif文の処理でnullを返している気がしたので修正しておきました. お付き合いいただき,ありがとうございました.
runny_nose

2018/10/30 09:02

>ユーティリティクラスのif文の処理でnullを返している気がしたので修正しておきました。 おお・・・たしかに・・・。すみません・・・。 正しくは、 × if (File.Exists(path)) 〇 if (!File.Exists(path)) でした。引数に指定されたパス(ファイル)が存在しない場合にnullを返すようにしています。 回答文も修正しておきました。
sicnweouif

2018/10/31 02:34

修正ありがとうございます.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問