c# - How to get specific values out of a DataTable using LINQ - c#

I have an Access table which looks like this:
ID | col_1 | col_2 | col_n
1 | 12345 | ... | ...
1 | null | ... | ...
1 | null | ... | ...
2 | 67891 | ... | ...
What I want to accomplish is to get all col_1 with the ID 1 if there is at least one value in col_1 with that ID. So my result would be:
ID | col_1
1 | 12345
1 | null
1 | null
The following code gets me the all the values of ID and col_1 and stores them in a DataTable results0.
public void ConnectDB(string path, string query0, string query1)
{
string connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + path + ";Persist Security Info=False";
try
{
using (OleDbConnection conn = new OleDbConnection(connString))
{
DataTable results0 = new DataTable();
OleDbCommand cmd = new OleDbCommand(query0, conn);
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
adapter.Fill(results0);
}
}
catch (System.InvalidOperationException inv)
{
MessageBox.Show(inv.Message);
throw;
}
}
I wanted to use LINQ for this issue, since I don't want to loop through the rows and tried a few things without success. At first I thought something like this would give me the relevant values (which it does)
int id = 1;
for (int i = 0; i < 9; i++) // iterate through IDs and increment
{
IEnumerable<String> Ids =
results0
.AsEnumerable()
.Where(row => row.Field<Int32>("ID") == id)
.Select(row => row.Field<String>("FERI"));
id+=1;
}
but I'm not sure how to rephrase it in an if-statement. Something like "If ID = 1 and at least one value in col_1 get range of rows with ID = 1"
I hope this isn't too confusing.
Any help and suggestions are appreciated!
Update: I'm still having trouble getting the relevant rows. I tried using DataRow[], selecting all the rows with ID = 1 and iterating with foreach-loops but this doesn't seem really efficient. Can anyone help?

To get the list of records with ID==1 from the Database assuming database with name "DBName", we will have:
public DBName _dbContext = new DBName ();
and then using following LINQ query we will get result:
_dbContext.TableName.Where(u => u.ID == 1).Select(u => u.col_1 ).ToList();

Real easy.
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("col_1", typeof(int));
dt.Columns["col_1"].AllowDBNull = true;
dt.Rows.Add(new object[] { 1, 12345});
dt.Rows.Add(new object[] { 1, null});
dt.Rows.Add(new object[] { 1, null});
dt.Rows.Add(new object[] { 2, 67891});
int id = 1;
DataTable dt2 = dt.AsEnumerable().Where(x => x.Field<int>("ID") == id).CopyToDataTable();

Related

Linq to datatable with unknown columns

I am having SQL query like this (will be parametrized once I make this work):
using (FbCommand cmd = new FbCommand("SELECT MAGACINID, ROBAID, SUM(KOLICINA) AS GOD" + godina.ToString() + " FROM STAVKA WHERE VRDOK = 13 OR VRDOK = 15 GROUP BY MAGACINID, ROBAID ORDER BY ROBAID", con))
{
using (FbDataAdapter da = new FbDataAdapter(cmd))
{
DataTable tempDT = new DataTable();
if (dt.Rows.Count == 0)
{
da.Fill(dt);
continue;
}
da.Fill(tempDT);
var result = dt.AsEnumerable()
.Join(tempDT.AsEnumerable(),
x => new { field1 = x["MAGACINID"], field2 = x["ROBAID"] },
y => new { field1 = y["MAGACINID"], field2 = y["ROBAID"] },
(x, y) => new {
x,
addYear = y["GOD" + godina.ToString()]
});
dt = LINQResultToDataTable(result);
}
}
And here is LINQResultToDataTable() method
private DataTable LINQResultToDataTable<T>(IEnumerable<T> Linqlist)
{
DataTable dt = new DataTable();
PropertyInfo[] allColumns = null;
if (Linqlist == null) return dt;
foreach (T Record in Linqlist)
{
if (allColumns == null)
{
allColumns = ((Type)Record.GetType()).GetProperties();
// Record at position 0 is whole object and needs to be broke into pieces
// Record at position 1 is new column
foreach (PropertyInfo GetProperty in allColumns)
{
Type colType = GetProperty.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()
== typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dt.Columns.Add(new DataColumn(GetProperty.Name, colType));
}
}
DataRow dr = dt.NewRow();
foreach (PropertyInfo pinfo in allColumns)
{
dr[pinfo.Name] = pinfo.GetValue(Record, null) == null ? DBNull.Value : pinfo.GetValue
(Record, null);
}
dt.Rows.Add(dr);
}
return dt;
}
First datatable fill is okay and it fills it with these columns:
MagacinID
RobaID
God2021
Now when I try to join new table using linq I should only add God2020 as new column.
Problem is that when I am creating output of linq I cannot tell it to break x object into columns but I need to take it whole so linq result looks like this:
x (this one have ItemArray with all columns)
God2020
And new datatable looks same.
What I tried doing is inside LINQResultToDataTable method to take 0th element of Result and break it into columns, add to datatable, add new one (1st element) and populate it (see comment in that method) but I am stuck and do not know how to do that. Anyone have any solution?
Edit: I made it not clear so here is more explanation.
I have foreach loop which loops through 5 databases and inside that foreach loop is this code.
First database returns these columns
MagacinID
RobaID
God2021
Second
MagacinID
RobaID
God2020
Third
MagacinID
RobaID
God2019
Fourth
MagacinID
RobaID
God2018
Fifth
MagacinID
RobaID
God2017
I want all this merged into one datatable on columns MagacinID AND RobaID like this:
MagacinID
RobaID
God2021
God2020
God2019
God2018
God2017
By Merge, I mean use the DataTable merge, which basically does something like a full outer join on declared PK fields in a datatable, and then makes the merged-into table the result of the join;
var d1 = new DataTable();
d1.Columns.Add("SomeId");
d1.Columns.Add("Col1");
d1.Rows.Add("ID1", "Col1Val1");
d1.Rows.Add("ID2", "Col1Val2");
d1.PrimaryKey = new[] { d1.Columns["SomeId"] };
var d2 = new DataTable();
d2.Columns.Add("SomeId");
d2.Columns.Add("Col2");
d2.Rows.Add("ID1", "Col2Val1");
d2.Rows.Add("ID3", "Col2Val2");
d2.PrimaryKey = new[] { d2.Columns["SomeId"] };
var d3 = new DataTable();
d3.Columns.Add("SomeId");
d3.Columns.Add("Col3");
d3.Rows.Add("ID2", "Col3Val1");
d3.Rows.Add("ID3", "Col3Val2");
d3.PrimaryKey = new[] { d3.Columns["SomeId"] };
d1.Merge(d2, false, MissingSchemaAction.Add);
d1.Merge(d3, false, MissingSchemaAction.Add);
Here's what the above code leads to:
d1 started off with two columns, then during the first Merge it added in a third (Col2), and the values from d2's Col2 were put into d1's Col2, matching on the PK column (SomeId).
Then during the second Merge a fourth column was added to d1 (Col3), and the values out of Col3 in d3 copied into d1.
You'll notice that I deliberately made d2 have different PK values to d1, so not only do new columns get added, but new rows get added too
No new rows were added by this second merge op (d1 already had an ID1, ID2 and ID3)

Combine two data tables with null values on both tables - C#

I have two data tables as shown below.
datatable1:
table1_id(PK) DriverID Vehicle
111 Ram00 VRN01
112 Shyam00 VRN02
113 Ram00 VRN03
datatable2:
table2_id(PK) DriverID exit_time
AA1 Ram00 10.10AM
AA2 Hari00 11.20PM
Combined Output
table1_id DriverID Vehicle table2_id exit_time
111 Ram00 VRN01 AA1 10.10AM
112 Shyam00 VRN02 NULL NULL
113 Ram00 VRN03 AA1 10.10AM
NULL Hari00 NULL AA2 11:20PM
DriverID is common in both table. But just merging two datatable will not give this result.
Please help to achieve this.
datatable1.Merge(datatable2);
You can use Two Data tables to combine into one Data table via Coding and Remove the Extra Column later the For loop ends,Check my code it will work.
string Qry = "select tab1.table_id,'' as DriverID,vehicle,tab1.driver_id Tab1DrvrID,exit_time from tab1 " +
"full join tab2 on tab2.driver_id=tab1.driver_id";
cmd = new SqlCommand(Qry, con);
da = new SqlDataAdapter(cmd);
dt = new DataTable();
da.Fill(dt);
//string DrvrID;
for (int i = 0; i < dt.Rows.Count; i++)
{
string Qry2 = "select tab1.table_id,'' as DriverID,vehicle,tab1.driver_id Tab1DrvrID,tab2.driver_id Tab2DrvrID,exit_time from tab1 " +
"full join tab2 on tab2.driver_id=tab1.driver_id ";
cmd = new SqlCommand(Qry2, con);
SqlDataAdapter daa = new SqlDataAdapter();
DataTable dtt = new DataTable();
daa = new SqlDataAdapter(cmd);
daa.Fill(dtt);
if (dtt.Rows.Count > 0)//
{
string s=dtt.Rows[i]["Tab1DrvrID"].ToString();
if (s=="")
{
dt.Rows[i]["DriverID"] = dtt.Rows[i]["Tab2DrvrID"].ToString();
}
else
{
dt.Rows[i]["DriverID"] = dtt.Rows[i]["Tab1DrvrID"].ToString();
}
}
else
{
}
dt.AcceptChanges();
}
dt.Columns.Remove("Tab1DrvrID");
Merge works properly if columns from two DATATABLES are matched in the same DATATYPE. If the column has a NULL value in the first row of DATATABLE the column DATATYPE will be String. So the second DATATABLE, if have value Date or any other type, will miss the match on merge . To resolve this problem you need to make the two DATATABLES columns same DATATYPE.
private DataTable MergeDataTable(DataTable dataTable1, DataTable dataTable2)
{
var dtCloned = dataTable2.Clone();
foreach (DataColumn column in dataTable2.Columns)
{
var col = dataTable1.Columns[column.ColumnName];
if (col != null && col.DataType != column.DataType )
{
dtCloned.Columns[column.ColumnName].DataType = col.DataType;
}
}
foreach (DataRow d in dataTable2.Rows)
{
dtCloned.ImportRow(d);
}
dataTable1.Merge(dtCloned);
return dataTable1;
}

Replace values from Select query in SQL using C#

My SQL query is:
GridView1.DataSource = GetData();
GridView1.DataBind();
DataTable GetData()
{
DataTable dt = new DataTable();
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["OfficeConnection"].ConnectionString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Consulting ", con))
{
SqlDataAdapter adpt = new SqlDataAdapter(cmd);
adpt.Fill(dt);
}
}
return dt;
}
I have a table with a few columns and what I am trying to achieve is to display the data in a GridView. At the moment the data is displayed in my GridView but I would like to replace text in the columns and then display in the gridview
So for example this is my table:
| DisplayName | $_License
+-------------+------------------------
| User 1 | TestLicense:License1 |
| User 2 | TestLicense:License2 |
So in my Gridview I would to display:
| Display Name | License Type |
+--------------+------------------------+
| User 1 | License1 |
| User 2 | License2 |
Note that theDisplay Name has a space and the $_License is changed to License Type and the row is changed from TestLicense:License1 to License1
Any help will be greatly appreciated.
Use this query:
"SELECT DisplayName 'Display Name', Replace($_License,'TestLicense:', '') 'License Type' Name FROM Consulting "
Modify your select query as per required result:
SELECT DisplayName AS 'Display Name' , RIGHT($_License, LEN($_License) - 11) AS 'License Type' FROM Consulting;

some selected fields as Pivote column ASP.NET

I want to show some selected columns as my SQL column and the rest of the column should be pivot. My output should be: Please help me any idea ?
Pivot table
ID | Employee_ID | 01-sep-2019 | 02-sep-2019 | 03-sep-2019
───┼─────────────┼─────────────┼─────────────┼────────────
1 | 1001 | P | A | P
2 | 1002 | A | P | A
3 | 1003 | A | P | P
Original table
ID | Employee_ID |STATUS | Created_Date
───┼─────────────┼───────┼─────────────
1 | 1001 | P | 01-sep-2019
2 | 1002 | A | 02-sep-2019
3 | 1003 | P | 03-sep-2019
I use 2 `GridView to show data but it's applicable for all column that I don't need. Could you please help me?
private DataTable PivotTable(DataTable origTable) {
DataTable newTable = new DataTable();
DataRow dr = null;
//Add Columns to new Table
for (int i = 0; i <= origTable.Rows.Count; i++) {
newTable.Columns.Add(new DataColumn(origTable.Columns[i].ColumnName, typeof(String)));
}
//Execute the Pivot Method
for (int cols = 0; cols < origTable.Columns.Count; cols++) {
dr = newTable.NewRow();
for (int rows = 0; rows < origTable.Rows.Count; rows++) {
if (rows < origTable.Columns.Count) {
dr[0] = origTable.Columns[cols].ColumnName; // Add the Column Name in the first Column
dr[rows + 1] = origTable.Rows[rows][cols];
}
}
newTable.Rows.Add(dr); //add the DataRow to the new Table rows collection
}
return newTable;
}
private void BindGridView() {
string strConnString = ConfigurationManager.ConnectionStrings["SQLDBConnection"].ConnectionString;
SqlConnection con = new SqlConnection(strConnString);
try {
con.Open();
string sqlStatement = "SELECT Top(5)* FROM tbl_QC_Attandence";
SqlCommand sqlCmd = new SqlCommand(sqlStatement, con);
SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCmd);
DataTable dt = new DataTable();
sqlDa.Fill(dt);
if (dt.Rows.Count > 0) {
//Bind the First GridView with the original data from the DataTable
grdorignal.DataSource = dt;
grdorignal.DataBind();
//Pivot the Original data from the DataTable by calling the
//method PivotTable and pass the dt as the parameter
DataTable pivotedTable = PivotTable(dt);
grdpivote.DataSource = pivotedTable;
grdpivote.DataBind();
}
} catch (System.Data.SqlClient.SqlException ex) {
string msg = "Fetch Error:";
msg += ex.Message;
throw new Exception(msg);
} finally {
con.Close();
}
}
ORIGINAL TABLE
ID Employee_ID STATUS Created_Date
1 1001 P 01-sep-2019
2 1002 A 02-sep-2019
3 1003 P 03-sep-2019
PIVOT TABLE
ID Employee_ID 01-sep-2019 02-sep-2019 03-sep-2019
1 1001 P A P
2 1002 A P A
3 1003 A P P
I have create a dynamic query which can help you, but null can be replaced with 'A' in code side, try below one
DECLARE
#columns NVARCHAR(MAX) = '',
#sql NVARCHAR(MAX) = '',
#SelectColumnNames AS NVARCHAR(MAX);
SELECT
#columns += QUOTENAME([Created_Date]) + ','
FROM
Employee
ORDER BY
[Created_Date];
SET #columns = LEFT(#columns, LEN(#columns) - 1);
Select #SelectColumnNames = ISNULL(#SelectColumnNames + ',','')
+ 'ISNULL(' + QUOTENAME([STATUS]) + ', 0) AS '
+ QUOTENAME([STATUS])
from (SELECT distinct [STATUS] from Employee) as Employees
print #SelectColumnNames
SET #sql =
N'Select * from(
select Created_Date,[STATUS],ID,Employee_ID
from Employee
)t
PIVOT(
MAX([STATUS])
FOR [Created_Date] IN ('+ #columns +')
) AS pivot_table
';
EXECUTE sp_executesql #sql;

Reference data in SQLDataReader by column and rows or any alternative

I am running a stored procedure and the result is this format
+------+--------+-----+-------+
| ID | Resign | Sum | Count |
+------+--------+-----+-------+
| 1234 | 0 | 400 | 3 |
| 1234 | 1 | 800 | 4 |
+------+--------+-----+-------+
I tried this code to reference the values returned by the query but, it seem not working the way I want it
if (conn.State != ConnectionState.Open)
conn.Open();
SqlCommand sc = new SqlCommand();
sc.CommandText = "usp_GetResignPool";
sc.CommandType = CommandType.StoredProcedure;
sc.Connection = conn;
sc.Parameters.Add(AddParam(EndDate, "#EndDate"));
sc.Parameters.Add(AddParam(am_id, "#id"));
SqlDataReader reader;
reader = sc.ExecuteReader();
while (reader.Read())
{
if reader. // lost here
}
How can I do something like this. ↓
int resigned = 0, resign_count = 0, not_resigned = 0, notresign_count =0;
if (read["Resigned"] == 1)
{
resigned = read["sum"];
resign_count = read["count"];
}
else
{
not_resigned = read["sum"];
notresign_count = read["count"];
}
It is not important that I used SQLDataReader.
Edit: Real column names
ID Resigned sum count
--------- ----------- ---------------------- -----------
It didn't work because you don't have a column in your table named "Resigned", like you have when you are working with your SqlDataReader.
EDIT: I think the root of the problem is the way you are adding parameters. AddParam() is not the method you want to be using. Therefore, your result set is probably empty.
....
SqlCommand sc = new SqlCommand();
sc.CommandText = "usp_GetResignPool";
sc.CommandType = CommandType.StoredProcedure;
sc.Connection = conn;
sc.Parameters.AddWithValue("#EndDate", EndDate);
sc.Parameters.AddWithValue("id", am_id);
SqlDataReader reader;
reader = sc.ExecuteReader();
using (reader = sc.ExecuteReader())
{
while (reader.Read())
{
if (Convert.ToInt32(read["Resign"]) == 1)
{
resigned = Convert.ToInt32(read["Sum"]);
resign_count = Convert.ToInt32(read["Count"]);
}
else
{
not_resigned = Convert.ToInt32(read["Sum"]);
notresign_count = Convert.ToInt32(read["Count"]);
}
}
}
Notice how I changed your element indicator to "Resign". This needs to match the column that is returned in your dataset. Or, you could use a column number to get this, like so:
if (Convert.ToInt32(read[1]) == 1)
{
resigned = Convert.ToInt32(read[2]);
resign_count = read[3];
}
else
{
not_resigned = Convert.ToInt32(read[2]);
notresign_count = Convert.ToInt32(read[3]);
}
Also, keep in my that in every iteration or your while loop, you'll be overwriting the variables resigned, resign_count or not_resigned and notresign_count.
Would this work?
int resign = 0;
int not_resign = 0;
int resign_count = 0;
int not_resign_count = 0;
while (reader.Read())
{
if (Convert.ToInt32(reader["Resigned"]) == 1)
{
resign = Convert.ToInt32(reader["Sum"]);
resign_count = Convert.ToInt32(reader["Count"]);
}
else
{
not_resign = Convert.ToInt32(reader["Sum"]);
not_resign_count = Convert.ToInt32(reader["Count"]);
}
}
Can you post your query from the procedure?
Are the column names really "Sum" and "Count"?
There are reserved words, maybe you should try using "AS" and give other names to these to columns in the projection.

Categories