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

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

ただいまの
回答率

91.37%

  • VB.NET

    711questions

    Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

  • ASP.NET

    389questions

    ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

  • Visual Studio 2010

    112questions

    Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

  • DataSet

    18questions

    DataSetは、ADO.NETアーキテクチャのコンポーネントです。データベースから取得したレコードをメモリ領域に格納するクラスを指します。データの保持やテーブル間のリレーション・制約といった保持も可能です。

DataTableを片側外部結合したい(Left Joinのような)

解決済

回答 1

投稿 2017/11/24 12:13

  • 評価
  • クリップ 0
  • VIEW 77

meesa

score 1

前提・実現したいこと

ASP.NetでWEB画面を開発しています。
データベースやCSVに出力されているデータを元にGridViewを作成し、
ひとつのテーブルを表示させる画面を開発中です。

データは複数に分かれており、PG内で情報を結合する必要があります。
テーブル1に表示されているすべての行に対し、
テーブル2のユーザーIDの一致する行の、システム利用権限の情報を表示しようとしています。

・テーブル1 ←Oracle DB
ユーザーID 部署 氏名
==============
ID0001 部署A 鈴木太郎
ID0002 部署A 山田一郎
ID0003 部署B 佐藤花子

・テーブル2 ←CSVファイル
ユーザーID システム利用権限
==============
ID0001 ○
ID0003 ×
ID0004 ○

テーブル1と2は、どちらか片方にしか情報のないユーザーの情報が含まれていますが、
上記のような場合、下記のようなデータを取り出したいです。

ユーザーID 部署 氏名 システム利用権限
=================
ID0001 部署A 鈴木太郎 ○
ID0002 部署A 山田一郎 Null
ID0003 部署B 佐藤花子 ×

発生している問題・エラーメッセージ

下記の質問の回答を元に、Relationを作成しようとするのですが、
DataSet.Relations.Addでリレーションを追加するところでエラーとなってしまいます。
https://teratail.com/questions/65331

ArgumentExceptionはユーザーコードによってハンドルされませんでした:
対応する親の値が指定されてない値があるため、この制約を有効にできません。

該当のソースコード

'戻り値用
Dim Kekka As New DataSet
Dim FullUserList As New DataTable

Dim UserInfoSet As New DataSet
Dim Tb1List As New DataTable
Dim Tb2List As New DataTable

'テーブル1をUserInfoSetのDataTableに格納
UserInfoSet = GetTb1List()
Tb1List= UserInfoSet.Tables(TB_USRINFO)

'テーブル2をUserInfoSetのDataTableに格納し、UserInfoSetに追加
Tb2List = GetTb2List()
UserInfoSet.Tables.Add(Tb2List)

'リレーションを作成
UserInfoSet.Relations.Add(New DataRelation("TEST", _
Tb1List .Columns("UserID"), _
Tb2List .Columns("UserID")))  'ここでエラー発生

'結合結果のDataTableの名前を定義
FullUserList.TableName = TB_FULLUSERLIST

'フル項目分のカラムを作成
FullUserList.Columns.Add("USERID")
FullUserList.Columns.Add("SECTNM")
FullUserList.Columns.Add("USERNM")
FullUserList.Columns.Add("Kengen")

'テーブルに値を追加
For Each r As DataRow In UserInfoList.Rows 

            Dim t2r As DataRow = r.GetParentRow("TEST") 
            FullUserList.Rows.Add(r.Item("USERID"), r.Item("SECTNM"), _
                                  r.Item("USERNM"), t2r.Item("Kengen"))

Next

'新規に作成したテーブルを返却用データセットに格納
Kekka.Tables.Add(FullUserList)

試したこと

Tb1ListとTb2Listには、それぞれ値がセットできていることは確認済みです。
それぞれ単独ではGridViewへバインドができ、表示結果もデータと一致しています。
なお、Tb2ListのCSVファイルは、Schema.iniファイルでカラム名と型を定義しています。

補足情報(言語/FW/ツール等のバージョンなど)

ASP.Net (VB) .NetFramework4.0
Visual Studio 2010

どなたか回答、アドバイスなどお待ちしています。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

下記の質問の回答を元に、Relationを作成しようとするのですが

リレーションを張るのが目的ではなくて、「テーブル1」と「テーブル2」を左外部結合した結果を GridView に表示できれば良いということであれば・・・

DataSet / DataTable を作ってそれを操作しようとするより、以下のように List<T> 型として「テーブル1」と「テーブル2」のレコードを取得し、Linq を使って結合した方がよさそうです。

左外部結合の実行
https://docs.microsoft.com/ja-jp/dotnet/csharp/linq/perform-left-outer-joins

【追記】

上記案でのサンプルを書いておきます。Oracle の方は Entity Framework を使えれば簡単に、CSV の方も DataSet / DataTable を作るのよりは多分少ない労力で List<T> 型のオブジェクトを作れると思います。

<%@ Page Language="C#" %>

<!DOCTYPE html>

<script runat="server">

    public class Employee
    {
        public string Id { get; set; }
        public string Dept { get; set; }
        public string Name { get; set; }
    }

    public class Role
    {
        public string Id { get; set; }
        public string Right { get; set; }
    }

    public class JoinedTable
    {
        public string Id { get; set; }
        public string Dept { get; set; }
        public string Name { get; set; }
        public string Right { get; set; }
    }

    protected void Page_Load(object sender, EventArgs erg)
    {
        if (!IsPostBack)
        {
            List<Employee> employees = new List<Employee>()
            {
                new Employee() { Id = "ID0001", Dept = "部署A", Name = "鈴木太郎" },
                new Employee() { Id = "ID0002", Dept = "部署A", Name = "山田一郎" },
                new Employee() { Id = "ID0003", Dept = "部署B", Name = "佐藤花子" }
            };

            List<Role> roles = new List<Role>()
            {
                new Role() { Id = "ID0001", Right ="〇" },
                new Role() { Id = "ID0003", Right ="×" },
                new Role() { Id = "ID0004", Right ="〇" }
            };

            var query = from e in employees
                        join r in roles on e.Id equals r.Id into gj
                        from sub in gj.DefaultIfEmpty()
                        select new JoinedTable
                        {
                            Id = e.Id,
                            Dept = e.Dept,
                            Name = e.Name,
                            Right = sub?.Right ?? string.Empty
                        };

            GridView1.DataSource = query;
            GridView1.DataBind();
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" runat="server"></asp:GridView>
    </div>
    </form>
</body>
</html>

【追記2】

VB.NET に直したサンプルを下にアップしておきます。変換サービスで変換して動くように手直ししただけです。なので、VB.NET の書き方としてはアレかもしれませんが、期待通りの結果になることは確認済みです。

<%@ Page Language="VB" %>

<!DOCTYPE html>

<script runat="server">

    Public Class Employee
        Public Property Id() As String
            Get
                Return m_Id
            End Get
            Set
                m_Id = Value
            End Set
        End Property
        Private m_Id As String
        Public Property Dept() As String
            Get
                Return m_Dept
            End Get
            Set
                m_Dept = Value
            End Set
        End Property
        Private m_Dept As String
        Public Property Name() As String
            Get
                Return m_Name
            End Get
            Set
                m_Name = Value
            End Set
        End Property
        Private m_Name As String
    End Class

    Public Class Role
        Public Property Id() As String
            Get
                Return m_Id
            End Get
            Set
                m_Id = Value
            End Set
        End Property
        Private m_Id As String
        Public Property Right() As String
            Get
                Return m_Right
            End Get
            Set
                m_Right = Value
            End Set
        End Property
        Private m_Right As String
    End Class

    Public Class JoinedTable
        Public Property Id() As String
            Get
                Return m_Id
            End Get
            Set
                m_Id = Value
            End Set
        End Property
        Private m_Id As String
        Public Property Dept() As String
            Get
                Return m_Dept
            End Get
            Set
                m_Dept = Value
            End Set
        End Property
        Private m_Dept As String
        Public Property Name() As String
            Get
                Return m_Name
            End Get
            Set
                m_Name = Value
            End Set
        End Property
        Private m_Name As String
        Public Property Right() As String
            Get
                Return m_Right
            End Get
            Set
                m_Right = Value
            End Set
        End Property
        Private m_Right As String
    End Class


    Protected Sub Page_Load(sender As Object, erg As EventArgs)
        If Not IsPostBack Then
            Dim employees As New List(Of Employee)() From {
                New Employee() With {
                                .Id = "ID0001",
                                .Dept = "部署A",
                                .Name = "鈴木太郎"
                },
                New Employee() With {
                                .Id = "ID0002",
                                .Dept = "部署A",
                                .Name = "山田一郎"
                },
                New Employee() With {
                                .Id = "ID0003",
                                .Dept = "部署B",
                                .Name = "佐藤花子"
                }
            }

            Dim roles As New List(Of Role)() From {
                New Role() With {
                            .Id = "ID0001",
                            .Right = "〇"
                },
                New Role() With {
                            .Id = "ID0003",
                            .Right = "×"
                },
                New Role() With {
                            .Id = "ID0004",
                            .Right = "〇"
                }
            }

            Dim query = From e In employees
                        Group Join r In roles
                        On e.Id Equals r.Id Into Group
                        From subrole In Group.DefaultIfEmpty()
                        Select New JoinedTable() With {
                            .Id = e.Id,
                            .Dept = e.Dept,
                            .Name = e.Name,
                            .Right = If(subrole Is Nothing, String.Empty, subrole.Right)
                        }

            GridView1.DataSource = query
            GridView1.DataBind()
        End If
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" runat="server"></asp:GridView>
    </div>
    </form>
</body>
</html>

投稿 2017/11/24 12:36

編集 2017/11/24 17:58

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/24 16:29 編集

    回答ありがとうございます。お返事遅れておりすみません。
    お恥ずかしながらVBでの開発しか経験がなく、C#コードがほぼ読めないため、
    List<T>の作成とLINQの扱いに苦労しております。
    (社内の情シス部門で細々と開発しています。部署の方針で、内製開発はVBに統一されているため、C#に触れる機会もほぼない環境です)
    時間がかかりそうですが、いただいたヒントを元にトライしてみます。
    うまくいきましたらご報告させていただきます。ありがとうございます。

    キャンセル

  • 2017/11/24 17:11 編集

    変換サービスがあるので使ってみてください。

    Convert Code
    http://converter.telerik.com/

    ただし、

    Right = sub?.Right ?? string.Empty

    の sub?.Right が変換サービスでは理解できないようですので、とりあえず sub.Right として変換にかけてみてください。変換後の VB.NET のコードは、そのままコピペして使えるかどうかは分かりませんが、人が読んで理解できる程度にはなると思います。

    キャンセル

  • 2017/11/24 17:11 編集

    先ほどのサンプルコードを変換サービスに投入してみたのですが、エラーになってしまいます。
    エラーメッセージは以下でした。

    CONVERSION ERROR: Code could not be converted. Details:
    -- line 22 col 15: invalid TypeDecl
    Please check for any errors in the original code and try again.

    投入したのは<script runat="server">~</script>の間部分です。
    どのように対応したらよいでしょうか?

    キャンセル

  • 2017/11/24 17:13

    変換できるのは C# のコードだけですよ。C# でない部分のコードをコピペしたのでは? あと、上のコメントに書いたように sub?.Right が変換サービスでは理解できないようですので、とりあえず sub.Right として変換にかけてみてください。

    キャンセル

  • 2017/11/24 17:30

    Page_Loadの中だけでかけてもエラーになってしまいます。
    sub.Rightに修正もしてありますが、エラー状況は変わりません。

    CONVERSION ERROR: Code could not be converted. Details:
    -- line 1 col 1: EOF expected
    Please check for any errors in the original code and try again.

    度々すみませんが、ご確認お願いします。

    キャンセル

  • 2017/11/24 17:52

    自分でも試しましたが、変換は問題なかったです。使えるようにするにはかなりの手直しが必要でしたけど、人間が見て分かる程度にはなります。そこのところを話していても時間がかかるばかりですので、VB.NET に変更したサンプルを回答欄に追記しておきます。

    キャンセル

  • 2017/11/27 09:28

    いただいたVBのサンプルソースにて、無事にやりたいことを実現することができました!
    LINQ部分の記述に困り果てておりましたので、VB版ソースで書いていただいたのでようやく理解ができました。本当に助かりました。ありがとうございました。

    キャンセル

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

ただいまの回答率

91.37%

関連した質問

同じタグがついた質問を見る

  • VB.NET

    711questions

    Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

  • ASP.NET

    389questions

    ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

  • Visual Studio 2010

    112questions

    Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

  • DataSet

    18questions

    DataSetは、ADO.NETアーキテクチャのコンポーネントです。データベースから取得したレコードをメモリ領域に格納するクラスを指します。データの保持やテーブル間のリレーション・制約といった保持も可能です。