2010-09-09 8 views
2

Je suis en train de LINQ deux tables basées sur une clé dynamique. L'utilisateur peut changer de clé via une zone de liste déroulante. La clé peut être de l'argent, de la ficelle, du double, de l'int, etc. Actuellement, je reçois très bien les données, mais sans filtrer les doubles. Je peux filtrer le double dans VB, mais c'est slooooow. Je voudrais le faire dans la requête LINQ dès la sortie de la porte.Linq se joindre sur la touche distincte paramétrés

est ici les données:

Première table:

------------------------------------------------------------- 
| AppleIndex | AppleCost | AppleColor | AppleDescription | 
------------------------------------------------------------ 
|  1  |  3  | Red   | This is an apple | 
|  2  |  5  | Green  | This is an apple | 
|  3  |  4  | Pink  | This is an apple | 
|  4  |  2  | Yellow  | This is an apple | 
|  5  |  2  | Orange  | This is an apple | 
|  1  |  3  | Red   | This is a duplicate| 
|  2  |  5  | Green  | This is a duplicate| 
|  3  |  4  | Pink  | This is a duplicate| 
|  4  |  2  | Yellow  | This is a duplicate| 
|  5  |  2  | Orange  | This is a duplicate| 
------------------------------------------------------------- 

Deuxième tableau:

------------------------------------------------------------ 
| OrangeIndex | OrangeCost | OrangeColor | OrangeDescription | 
------------------------------------------------------------ 
|  1  |  1  | Orange  | This is an Orange | 
|  2  |  3  | Orange  |     | 
|  3  |  2  | Orange  | This is an Orange | 
|  4  |  3  | Orange  |     | 
|  5  |  2  | Orange  | This is an Orange | 
------------------------------------------------------------ 

Actuellement, je suis en utilisant le code suivant pour obtenir trop données:

Dim Matches = From mRows In LinqMasterTable Join sRows In LinqSecondTable _ 
       On mRows(ThePrimaryKey) Equals sRows(TheForignKey) _ 
       Order By mRows(ThePrimaryKey) _ 
       Select mRows, sRows Distinct 

Résultat:

------------------------------------------------------------------------- 
| 1 | 3 | Red | This is an apple  | 1 | Orange | This is an Orange | 
| 1 | 3 | Red | This is an duplicate | 1 | Orange | This is an Orange | 
| 2 | 5 | Green | This is an apple  | 3 | Orange |     | 
| 2 | 5 | Green | This is an duplicate | 3 | Orange |     | 
| 3 | 4 | Pink | This is an apple  | 2 | Orange | This is an Orange | 
| 3 | 4 | Pink | This is an duplicate | 2 | Orange | This is an Orange | 
| 4 | 2 | Yellow | This is an apple  | 3 | Orange |     | 
| 4 | 2 | Yellow | This is an duplicate | 3 | Orange |     | 
| 5 | 2 | Orange | This is an apple  | 2 | Orange | This is an Orange | 
| 5 | 2 | Orange | This is an duplicate | 2 | Orange | This is an Orange | 
------------------------------------------------------------------------- 

Résultat souhaité:

------------------------------------------------------------------------ 
| 1 | 3 | Red | This is an apple | 1 | 1 | Orange | This is an Orange | 
| 2 | 5 | Green | This is an apple | 2 | 3 | Orange |     | 
| 3 | 4 | Pink | This is an apple | 3 | 2 | Orange | This is an Orange | 
| 4 | 2 | Yellow | This is an apple | 4 | 3 | Orange |     | 
| 5 | 2 | Orange | This is an apple | 5 | 2 | Orange | This is an Orange | 
------------------------------------------------------------------------ 

J'ai essayé ce qui suit:

'Get the original Column Names into an Array List 
'MasterTableColumns = GetColumns(qMasterDS, TheMasterTable) '(external code) 

'Plug the Existing DataSet into a DataView: 
Dim View As DataView = New DataView(qMasterTable) 

'Sort by the Primary Key: 
View.Sort = ThePrimaryKey 

'Build a new table listing only one column: 
Dim newListTable As DataTable = _ 
View.ToTable("UniqueData", True, ThePrimaryKey) 

Ceci retourne une liste unique, mais pas les données associées:

------------- 
| AppleIndex | 
------------- 
|  1  | 
|  2  | 
|  3  | 
|  4  | 
|  5  | 
------------- 

donc j'ai essayé ceci:

'Build a new table with ALL the columns: 
Dim newFullTable As DataTable = _ 
View.ToTable("UniqueData", True, _ 
    MasterTableColumns(0), _ 
    MasterTableColumns(1), _ 
    MasterTableColumns(2), _ 
    MasterTableColumns(3)) 

Malheureusement, il donne les éléments suivants ... avec doublons:

------------------------------------------------------------- 
| AppleIndex | AppleCost | AppleColor | AppleDescription | 
------------------------------------------------------------ 
|  1  |  3  | Red   | This is an apple | 
|  2  |  5  | Green  | This is an apple | 
|  3  |  4  | Pink  | This is an apple | 
|  4  |  2  | Yellow  | This is an apple | 
|  5  |  2  | Orange  | This is an apple | 
|  1  |  3  | Red   | This is a duplicate| 
|  2  |  5  | Green  | This is a duplicate| 
|  3  |  4  | Pink  | This is a duplicate| 
|  4  |  2  | Yellow  | This is a duplicate| 
|  5  |  2  | Orange  | This is a duplicate| 
------------------------------------------------------------- 

Toutes les idées?

~~~~~~~~~~~~ Mise à jour: ~~~~~~~~~~~~

Jeff M a suggéré le code suivant. (Merci Jeff) Cependant, cela me donne une erreur. Est-ce que quelqu'un sait la syntaxe pour faire ce travail en VB? Je l'ai singé avec un peu et n'arrive pas à faire les choses correctement.

Dim matches = _ 
    From mRows In (From row In LinqMasterTable _ 
     Group row By row(ThePrimaryKey) Into g() _ 
     Select g.First()) _ 
    Join sRows In LinqSecondTable _ 
    On mRows(ThePrimaryKey) Equals sRows(TheForignKey) _ 
    Order By mRows(ThePrimaryKey) _ 
    Select mRows, sRows 

erreur dans le troisième rang à "rangée (ThePrimaryKey)":

"Nom de la plage variable peut être déduite que d'un nom simple ou qualifié sans arguments"

+0

Est-il acceptable d'avoir parfois une ligne "Ceci est un doublon" au lieu de la ligne correspondante "Ceci est une pomme"? –

+0

Oui. Il est acceptable. –

Répondre

0

Déclarations et tel:

Private Sub LinqTwoTableInnerJoin(ByRef qMasterDS As DataSet, _ 
            ByRef qMasterTable As DataTable, _ 
            ByRef qSecondDS As DataSet, _ 
            ByRef qSecondTable As DataTable, _ 
            ByRef qPrimaryKey As String, _ 
            ByRef qForignKey As String, _ 
            ByVal qResultsName As String) 

Dim TheMasterTable As String = qMasterTable.TableName 
Dim TheSecondTable As String = qSecondTable.TableName 
Dim ThePrimaryKey As String = qPrimaryKey 
Dim TheForignKey As String = qForignKey 
Dim TheNewForignKey As String = "" 

MasterTableColumns = GetColumns(qMasterDS, TheMasterTable) 
SecondTableColumns = GetColumns(qSecondDS, TheSecondTable) 

Dim mColumnCount As Integer = MasterTableColumns.Count 
Dim sColumnCount As Integer = SecondTableColumns.Count 

Dim ColumnCount As Integer = mColumnCount + sColumnCount 

Dim LinqMasterTable = qMasterDS.Tables(TheMasterTable).AsEnumerable 
Dim LinqSecondTable = qSecondDS.Tables(TheSecondTable).AsEnumerable 

Obtenir les données et l'ordre par le sélectionné Clé:

Dim Matches = From mRows In LinqMasterTable Join sRows In LinqSecondTable _ 
      On mRows(ThePrimaryKey) Equals sRows(TheForignKey) _ 
      Order By mRows(ThePrimaryKey) _ 
      Select mRows, sRows 

Mettre les résultats dans un DataSet Tableau:

' Make sure the dataset is available and/or cleared: 
If dsResults.Tables(qResultsName) Is Nothing Then dsResults.Tables.Add(qResultsName) 
dsResults.Tables(qResultsName).Clear() : dsResults.Tables(qResultsName).Columns.Clear() 

'Adds Master Table Column Names 
For x = 0 To MasterTableColumns.Count - 1 
    dsResults.Tables(qResultsName).Columns.Add(MasterTableColumns(x)) 
Next 

'Rename Second Table Names if Needed: 
For x = 0 To SecondTableColumns.Count - 1 
    With dsResults.Tables(qResultsName) 
     For y = 0 To .Columns.Count - 1 
      If SecondTableColumns(x) = .Columns(y).ColumnName Then 
       SecondTableColumns(x) = SecondTableColumns(x) & "_2" 
      End If 
     Next 
    End With 
Next 

'Make sure that the Forign Key is a Unique Value 
If ForignKey1 = PrimaryKey Then 
    TheNewForignKey = ForignKey1 & "_2" 
Else 
    TheNewForignKey = ForignKey1 
End If 

'Adds Second Table Column Names 
For x = 0 To SecondTableColumns.Count - 1 
    dsResults.Tables(qResultsName).Columns.Add(SecondTableColumns(x)) 
Next 

'Copy Results into the Dataset: 
For Each Match In Matches 

    'Build an array for each row: 
    Dim NewRow(ColumnCount - 1) As Object 

    'Add the mRow Items: 
    For x = 0 To MasterTableColumns.Count - 1 
     NewRow(x) = Match.mRows.Item(x) 
    Next 

    'Add the srow Items: 
    For x = 0 To SecondTableColumns.Count - 1 
     Dim y As Integer = x + (MasterTableColumns.Count) 
     NewRow(y) = Match.sRows.Item(x) 
    Next 

    'Add the array to dsResults as a Row: 
    dsResults.Tables(qResultsName).Rows.Add(NewRow) 

Next 

Donner à l'utilisateur une option pour nettoyer en double ou non:

If chkUnique.Checked = True Then 
    ReMoveDuplicates(dsResults.Tables(qResultsName), ThePrimaryKey) 
End If 

Retirez les duplicatas si elles le souhaitent:

Private Sub ReMoveDuplicates(ByRef SkipTable As DataTable, _ 
         ByRef TableKey As String) 

    'Make sure that there's data to work with: 
    If SkipTable Is Nothing Then Exit Sub 
    If TableKey Is Nothing Then Exit Sub 

    'Create an ArrayList of rows to delete: 
    Dim DeleteRows As New ArrayList() 

    'Fill the Array with Row Number of the items equal 
    'to the item above them: 
    For x = 1 To SkipTable.Rows.Count - 1 
     Dim RowOne As DataRow = SkipTable.Rows(x - 1) 
     Dim RowTwo As DataRow = SkipTable.Rows(x) 
     If RowTwo.Item(TableKey) = RowOne.Item(TableKey) Then 
      DeleteRows.Add(x) 
     End If 
    Next 

    'If there are no hits, exit this sub: 
    If DeleteRows.Count < 1 Or DeleteRows Is Nothing Then 
     Exit Sub 
    End If 

    'Otherwise, remove the rows based on the row count value: 
    For x = 0 To DeleteRows.Count - 1 

     'Start at the END and count backwards so the duplicate 
     'item's row count value doesn't change with each deleted row 
     Dim KillRow As Integer = DeleteRows((DeleteRows.Count - 1) - x) 

     'Delete the row: 
     SkipTable.Rows(KillRow).Delete() 

    Next 
End Sub 

nettoyer ensuite les restes:

If Not chkRetainKeys.Checked = True Then 'Removes Forign Key 
    dsResults.Tables(qResultsName).Columns.Remove(TheNewForignKey) 
End If 

'Clear Arrays 
MasterTableColumns.Clear() 
SecondTableColumns.Clear() 

Analyse finale: contre 2 cette Ran Fichiers avec 4 colonnes, 65.535 lignes et avec quelques doubles. Temps de traitement, environ 1 seconde. En fait, il a fallu plus de temps pour charger les champs en mémoire que pour analyser les données.

0

Editer:
Voici comment j'écrirais la requête C# LINQ.Voici une version alternative plutôt que d'utiliser Distinct(), utilise une requête imbriquée avec un regroupement qui devrait avoir une sémantique similaire. Il devrait être facilement convertible en VB.

var matches = from mRows in (from row in LinqMasterTable 
          group row by row[ThePrimaryKey] into g 
          select g.First()) 
       join sRows in LinqSecondTable 
        on mRows[ThePrimaryKey] Equals sRows[TheForignKey] 
       orderby mRows[ThePrimaryKey] 
       select new { mRows, sRows } 

et ma tentative d'une version VB de ce qui précède:

Edit:
En ce qui concerne l'erreur la plus récente, je sais exactement comment y faire face. Quand je jouais avec VB LINQ, j'ai trouvé que le compilateur n'aime pas les expressions de regroupement complexes. Pour contourner cela, affectez row(ThePrimaryKey) à une variable temporaire et groupez par cette variable. Cela devrait fonctionner ensuite.

Dim matches = From mRows In (From row In LinqMasterTable _ 
          Let grouping = row(ThePrimaryKey) 
          Group row By grouping Into g() _ 
          Select g.First()) _ 
       Join sRows In LinqSecondTable _ 
        On mRows(ThePrimaryKey) Equals sRows(TheForignKey) _ 
       Order By mRows(ThePrimaryKey) _ 
       Select mRows, sRows 

En fait, lors de la deuxième inspection, il se trouve que ce qui est regroupé par les besoins d'un nom. Ce qui suit fonctionnera.

Dim matches = From mRows In (From row In LinqMasterTable _ 
          Group row By Grouping = row(ThePrimaryKey) Into g() _ 
          Select g.First()) _ 
       Join sRows In LinqSecondTable _ 
        On mRows(ThePrimaryKey) Equals sRows(TheForignKey) _ 
       Order By mRows(ThePrimaryKey) _ 
       Select mRows, sRows 
+0

Le problème est que toutes les lignes de LinqMasterTable * sont déjà distinctes. –

+0

Vrai, mais j'avais l'intention, distincte par clé primaire. Je ne sais pas comment l'exprimer en VB. –

+0

Je suppose que vous vouliez dire "clé donnée", puisque les clés primaires sont distinctes par nature. Et je ne pense pas que ce soit même exprimable en SQL: "Grouper par clé donnée, et sélectionner une rangée au hasard"? –

1

Eh bien, le problème de base n'est pas le LINQ. C'est le fait que votre Première Table contient des "doublons", qui ne sont pas vraiment des doublons, puisque dans votre exemple, chaque rangée est distinctive. Donc, notre question à vous est: "Comment identifier les doublons dans le tableau d'origine?". Une fois que cela est répondu, le reste devrait être trivial.

Par exemple (en C# puisque je ne suis pas sûr de la syntaxe VB)

var Matches = from mRows in LinqMasterTable 
          .Where(r=>r.AppleDescription=="This is an Apple") 
       join sRows in LinqSecondTable 
        on mRows(ThePrimaryKey) equals sRows(TheForignKey) 
       orderby mRows(ThePrimaryKey) 
       select new { mRows, sRows}; 
+0

D'accord. Pas une vraie clé primaire, c'est juste le nom de la chaîne, utilisée comme un mnémonique pour aider à garder une trace de ce que je fais pendant le codage. –