I have a chartTable with 2 columns:
ChildPersonID | ParentPersonID
--------------+-----------------
1 | 2
1 | 3
2 | 4
That is joined to personTable with 2 columns:
ID | PersonName
---+-----------------
1 | a
2 | b
3 | c
4 | d
I want a select query that fill datatable with PersonName that show in treeview
Result:
parentname | parentid | childname | childid
-----------+----------+-----------+---------
a | 1 | b | 2
a | 1 | c | 3
b | 2 | d | 4
my code
DECLARE #Table1 TABLE (ChildPersonID INT,ParentPersonID INT)
DECLARE #Table2 TABLE (ID INT, PersonName VARCHAR(10))
INSERT INTO #Table1 VALUES (1,2),(1,3),(2,4)
INSERT INTO #Table2 VALUES (1,'a'),(2,'b'),(3,'c'),(4,'d')
SELECT T3.PersonName AS parentName, T1.ChildPersonID AS ParentId,
T2.PersonName AS childname, T1.ParentPersonID AS childid
FROM #Table1 T1
INNER JOIN #Table2 T2 ON T1.ParentPersonID = T2.Id
INNER JOIN #Table2 T3 ON T2.ChildPersonID = T3.id
This is the query which you are looking for (though the column name which you have mentioned are confusing, I think it should be reversed)
DECLARE #Table1 TABLE (ChildPersonID INT,ParentPersonID INT)
DECLARE #Table2 TABLE (ID INT, PersonName VARCHAR(10))
INSERT INTO #Table1 VALUES (1,2),(1,3),(2,4)
INSERT INTO #Table2 VALUES (1,'a'),(2,'b'),(3,'c'),(4,'d')
SELECT T3.PersonName AS parentName, T1.ChildPersonID AS ParentId,
T2.PersonName AS childname, T1.ParentPersonID AS childid
FROM #Table1 T1
INNER JOIN #Table2 T2 ON T1.ParentPersonID = T2.Id
INNER JOIN #Table2 T3 ON T1.ChildPersonID = T3.id
See code below :
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication94
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("parentname", typeof(string));
dt.Columns.Add("parentid", typeof(int));
dt.Columns.Add("childname", typeof(string));
dt.Columns.Add("childid", typeof(int));
DataTable dtChildPerson = new DataTable();
dtChildPerson.Columns.Add("ChildPersonID", typeof(int));
dtChildPerson.Columns.Add("ParentPersonID", typeof(int));
dtChildPerson.Rows.Add(new object[] { 1, 1 });
dtChildPerson.Rows.Add(new object[] { 1, 3 });
dtChildPerson.Rows.Add(new object[] { 2, 4 });
DataTable personName = new DataTable();
personName.Columns.Add("ID", typeof(int));
personName.Columns.Add("PersonName", typeof(string));
personName.Rows.Add(new object[] { 1, "a" });
personName.Rows.Add(new object[] { 2, "b" });
personName.Rows.Add(new object[] { 3, "c" });
personName.Rows.Add(new object[] { 4, "d" });
foreach (DataRow row in dtChildPerson.AsEnumerable())
{
int parentID = row.Field<int>("ParentPersonID");
string parentName = personName.AsEnumerable().Where(x => x.Field<int>("ID") == parentID).Select(x => x.Field<string>("PersonName")).FirstOrDefault();
int childID = row.Field<int>("ChildPersonID");
foreach(DataRow childRow in personName.AsEnumerable().Where(x => x.Field<int>("ID") == childID))
{
string childName = childRow.Field<string>("PersonName");
dt.Rows.Add(new object[] { parentName, parentID, childName, childID });
}
}
}
}
}
Related
So here is the issue I am facing. I have a one to many list relationship.
Here are examples of how the lists would look.
The objects in list 1 are dynamic and have a few properties with the most important one being "id".
The second list has a defined object looking something like this "id" "value desc" "actual value".
The second list can have many rows belonging to the first list. The "value desc" is the property name and the "actual value".
I need to combine these lists into something that looks like this. An object with all list 1 properties and all corresponding rows in the second list. If the second list had 3 items belonging to an item in list 1 then the new object should have all properties of list 1 along with all the rows gathered from list 2 in a flat structure like way.
Data examples
Table 1:
id
name
1
bob
2
joe
Table 2
id
propname
value
1
length
2
1
age
12
1
haircolor
blue
2
length
5
2
age
90
2
haircolor
red
How I want the data to look
id
name
length
haircolor
age
1
bob
2
blue
12
2
joe
5
red
90
Currently, I have this working.
public IEnumerable<dynamic> test(List<dynamic> data, List<modal>
dataset)//closest
{
var query = (from a in data
join b in dataset
on a.id equals b.id into t
select new {
a,
t
});
return query;
}
However, the result is an object with properties of list 1 and then a property on that object that is an array of items found in list 2. I need these items to not be in an array and be property names with values on the new created object.
I hope my explanation was clear enough!
Use a pivolt table :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("id", typeof(int));
dt1.Columns.Add("name", typeof(string));
dt1.Rows.Add(new object[] { 1, "bob" });
dt1.Rows.Add(new object[] { 2, "bob" });
DataTable dt2 = new DataTable();
dt2.Columns.Add("id", typeof(int));
dt2.Columns.Add("propname", typeof(string));
dt2.Columns.Add("value", typeof(object));
dt2.Rows.Add(new object[] { 1, "length", 2 });
dt2.Rows.Add(new object[] { 1, "age", 12 });
dt2.Rows.Add(new object[] { 1, "haircolor", "blue"});
dt2.Rows.Add(new object[] { 2, "length", 5 });
dt2.Rows.Add(new object[] { 2, "age", 90 });
dt2.Rows.Add(new object[] { 2, "haircolor", "red" });
DataTable pivot = new DataTable();
string[] properties = dt2.AsEnumerable().Select(x => x.Field<string>("propname")).Distinct().OrderBy(x => x).ToArray();
pivot.Columns.Add("id", typeof(int));
pivot.Columns.Add("name", typeof(string));
DataColumn[] columns = properties.Select(x => new DataColumn(x, typeof(object))).ToArray();
pivot.Columns.AddRange(columns);
var joins = from id1 in dt1.AsEnumerable()
join id2 in dt2.AsEnumerable() on id1.Field<int>("id") equals id2.Field<int>("id")
select new { id = id1.Field<int>("id"), name = id1.Field<string>("name"), id2 = id2 };
var groups = joins.GroupBy(x => x.id);
foreach (var group in groups)
{
DataRow newRow = pivot.Rows.Add();
newRow["id"] = group.Key;
newRow["name"] = group.First().name;
foreach (var row in group)
{
newRow[row.id2.Field<string>("propname")] = row.id2.Field<object>("value");
}
}
}
}
}
What Im trying to do, is distinct (or group by) for one column, but then keeping the one in my List which has a value fo a second column.
What i have
Column1 Column 2 Column3 ...
1 | tada | smth
1 | | wefih
2 | tada | uitethgev
3 | | urifnvf
what i want
Column1 Column 2 Column3 ...
1 | tada | smth
2 | tada | uitethgev
3 | | urifnvf
As i only have one "3" i wanna keep it in my list. Same for the 2, but the 1 should only stay with a value in column2.
I wanna do this in a linq query. Each row is an Object with attributes that represent the columns.
Any clues on this? I know how to make it by using multiple lists and writing to each other with a method checking it. But i thought there could be a nice linq way to do this. Also keep in mind pls that i have more then just 3 columns.
You can do it by linq, specially by GroupBy why not :
1 - i'm creating a class that simulate your demand :
public class TestClass
{
public int Column1 { get; set; }
public string Column2 { get; set; }
public string Column3 { get; set; }
}
2 - i'm initializing a list of TestClass like a below:
List<TestClass> testClasses = new List<TestClass>
{
new TestClass{Column1 = 1, Column2 = "tada", Column3 = "smth"},
new TestClass{Column1 = 1, Column2 = "msa", Column3 = "msa1"},
new TestClass{Column1 = 1, Column3 = "wefih"},
new TestClass{Column1 = 2, Column2 = "tada", Column3 = "uitethgev"},
new TestClass{Column1 = 3, Column3 = "urifnvf"},
};
3 - using groupBy to filter your list, by testing the count of grouped element is grater than or equal 2 :
if count >= 2 : take the first element that have a column2 not empty
else : take element without filtering
List<TestClass> groupedList = testClasses
.GroupBy(x => x.Column1)
.Select(y => y.Count() >= 2 ? y.First(z => !string.IsNullOrEmpty(z.Column2)) : y.First())
.ToList();
Result of 3 :
Column1|Column 2| Column3 ...
1 | tada | smth
2 | tada | uitethgev
3 | | urifnvf
4 - if you need all not empty column2, if the count grater than 2, try this code :
List<TestClass> groupedList = testClasses
.GroupBy(x => x.Column1)
.SelectMany(y => y.Count() >= 2 ? y.TakeWhile(z => !string.IsNullOrEmpty(z.Column2)) : y)
.ToList();
Result of 4 :
Column1|Column 2| Column3 ...
1 | tada | smth
1 | msa | msa1
2 | tada | uitethgev
3 | | urifnvf
i hope that will give you an answer
The following linq works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Column1", typeof(int));
dt.Columns.Add("Column2", typeof(string));
dt.Columns.Add("Column3", typeof(string));
dt.Rows.Add(new object[] { 1, "tada", "smth"});
dt.Rows.Add(new object[] { 1, null, "wefih"});
dt.Rows.Add(new object[] { 2, "tada", "uitethgev"});
dt.Rows.Add(new object[] { 3, null, "erifnvf"});
DataTable dt2 = dt.AsEnumerable()
.GroupBy(x => x.Field<int>("Column1"))
.Select(x => x.All(y => (y.Field<object>("Column2") == null)) ? x.First() : x.Where(y => y.Field<object>("Column2") != null).First())
.CopyToDataTable();
}
}
}
I would like to log added records using Entity Framework
Table1
CatID | Col1 | Col2 | Col3 | Col4 |
----------------------------------
1 | a | b | c | d |
----------------------------------
Code for saving
var context = new DBEntities();
Table1 obj1 = new Table1{
Col1 = "a", Col2 = "b"
};
context.Table1.AddObject(obj1)
context.SaveChanges();
Dummy code for saving audit
var propWithValue = //get only Col1 and Col2
foreach(var list1 in propWithValue)
{
//save audit
}
How can i do this in EF4?
I have three tables I'm interested in, here's the relevant information:
+---------------+ +---------------+ +---------------+ +---------------+
| Class | | Tests | | StudentClasses| | Student |
+---------------+ +---------------+ +---------------+ +---------------+
| ClassID | | ClassID | | ClassID | | StudentID |
| | | WholeYearTest | | StudentID | | StudentYear |
| | | TestYear | | | | |
+---------------+ +---------------+ +---------------+ +---------------+
I have a variable login, which is a Student.
I am running this query in LINQ:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses on tests.ClassID equals studentClasses.ClassID
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
studentClasses.StudentID == login.StudentID
)
select tests;
Unfortunately, I'm only getting results which correspond to the condition studentClasses.StudentID == login.StudentID
What I want:
All tests that the student has to sit due to StudentYear being equal to TestYear and WholeYearTest being true
All tests that the student has to sit because one of the possibly multiple StudentClasses associated with them is listed under Tests.ClassID.
I think it's probably due to a blatant misunderstanding of how JOIN works, but I can't think of any other way to implement this. Can anyone put forward an implementation suggestion other than looping all tests? If not, I suppose I'll use a loop, but I'm sure there must be some way of implementing this in LINQ.
I think this will do what you want:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on new { tests.ClassID, login.StudentID }
equals new { studentClasses.ClassID, studentClasses.StudentID }
into gj
from subStudentClass in gj.DefaultIfEmpty()
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
(subStudentClass != null && subStudentClass.StudentID == login.StudentID)
)
select tests
The biggest difference is that there is now an outer join so that you can still find results where the join fails.
Also, the join is now by both ClassID and StudentID, so that you won't get matches for other students.
With the following test data:
var App = new { db = new {
Tests = new[] {
new Test { ClassID = 1, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 2, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 3, WholeYearTest = false, TestYear = 1999 },
new Test { ClassID = 4, WholeYearTest = false, TestYear = 1999 },
},
StudentClasses = new[] {
new StudentClass { ClassID = 1, StudentID = 1 },
new StudentClass { ClassID = 1, StudentID = 2 },
new StudentClass { ClassID = 4, StudentID = 1 },
new StudentClass { ClassID = 3, StudentID = 2 },
}
} };
var login = new { StudentID = 1, StudentYear = 1999 };
Console.WriteLine(string.Join(Environment.NewLine, (
//above query
).Select(x => string.Join(",", x.ClassID, x.WholeYearTest, x.TestYear))));
It prints
1,True,1999
2,True,1999
4,False,1999
A single inner join is not sufficient; your query actually needs to be executed with an inner join AND a union.
Give this a try - I didn't test it out because I figure you can do that :)
Note - you may need to insert a Distinct() in there as well.
var q1 = (from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on tests.ClassID equals studentClasses.ClassID
select tests).Concat(App.db.Tests.Where(t=>t.WholeYearTest && t.TestYear == login.StudentYear));
I have Datatable 1:-----------------------------should be like that:
ID Name Lastname ID Name Lastname
------------------- -----------------------
1 | koki ha 1 | koki ha
------------------- | ----------------- //merge Rows[0][0]
1 | lola mi | lola mi //with Rows[1][0] if the same
------------------- -----------------------
2 | ka xe 2 ka xe
how to replace "1" with "" or empty if is already exist? I spend for this for 2 hours but can't find the solution. I tried with linq but dont find the key to do it right, maybe distinct or group?
DataTable table = new DataTable("table");
table.Columns.Add("ID", typeof(Int32));
table.Columns.Add("Name", typeof(String));
table.Columns.Add("Lastname", typeof(String));
object[] o1 = { 1, "Kiki", "ha"};
object[] o2 = { 1,"lola","mi"};
object[] o4 = { 2, "ka", "xe" };
table.Rows.Add(o1);
table.Rows.Add(o2);
table.Rows.Add(o4);
dataGridView2.DataSource = table;
Here's how you can do this using LINQ:
var dataRows = table.Rows.Cast<System.Data.DataRow>()
.GroupBy(r => r[0])
.Where(g => g.Count() > 1);
foreach (var dataRowGroup in dataRows) {
int idx = 0;
foreach (DataRow row in dataRowGroup) {
if (idx++ > 0) {
row[0] = DBNull.Value;
}
}
}