Remove a column from select statement - c#

I would like to know is there a way to remove a specific column when displaying in a datagridview. Below is the sql statemnt to retrieve
OleDbDataAdapter oda = new OleDbDataAdapter("select * from BHR_2016_FEB_CIT4114_FYP_GD", con);
DataTable dt = new DataTable();
oda.Fill(dt);
dt.Columns.Remove("Fingerprint_Template");
dataGridViewAttendanceDatabase.DataSource = dt;
The reasons is because, i have a type of format CLOB, therefore it will not display in datagridview. So i plan remove a column which stores the CLOB format. I can specify, but the problem i will be selecting table based on combo box and each table have different number of columns such as a table might have 31 column, another table might have 28 column. So how i could remove only a single column. Thanks in advanced.
There is still an error as when i state select *, it includes the column which consist of CLOB format. Therefore error exist at oda.Fill(dt);. Is there a way to select * except a columns which consist of CLOB format. This help is really appreciated.

you can just do this
dt.Columns.Remove("xyz");
Update
It seems Fill does not support CLOB data. Above solution will not work.You need to do change in SQL itself. My suggestion is to move everything in stored procedure and use meta data to extract column which you want (or exclude column which you do not want.) Here is sample code. Please note I have not tested it so you may find some minor issue but the code gives you gist of what you can do to solve your problem
C# Code
OleDbConnection oc= new OleDbConnection("[pass your connection string]");
OleDbCommand ocom = new OleDbCommand();
ocom.CommandText = "Abc"; // Abc is stored procedure
ocom.Connection = oc;
ocom.CommandType = CommandType.StoreProcedure;
ocom.Parameters.AddWithValue("#tableName","PQR") // pass your table name
ocom.Parameters.AddWithValue("#databaseName","IJK"); // pass your database name
OleDbDataAdapter oda = new OleDbDataAdapter(ocom);
DataTable dt = new DataTable();
oda.Fill(dt);
dataGridViewAttendanceDatabase.DataSource = dt;
SQL Script
Create Porocedure Abc (#tableName varchar(255),#databaseName varchar(255))
Begin
SET #sql = CONCAT('SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'Fingerprint_Template,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tableName AND TABLE_SCHEMA = #databaseName), CONCAT(' FROM ',#tableName));
PREPARE stmt FROM #sql;
EXECUTE stmt;
End

Related

SQL INNER JOIN, #Parameter - does not filter results

Thank you in advance for any advice you are able to give me.
What I'm trying to achieve: I have a combobox that I want to populate with table1 / columnC, sorted by matches in table1 / columnB and table2 / columnB.
For example: I want to display all of Roberts potatoes, not everyone's potatoes.
I have created a stored procedure for this. It currently looks like this:
CREATE PROCEDURE [dbo].[myProcedure]
#myParameter nvarchar
AS
SELECT columnA, columnB, columnC
FROM table1
INNER JOIN table2 ON table1.columnB = table2.columnB
WHERE table1.columnB = #myParameter
Data set:
DataSet MyDataSet()
{
DataSet ds = new DataSet();
using (SqlConnection con = new SqlConnection(connectionString))
using (SqlDataAdapter da = new SqlDataAdapater("myProcedure", con))
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
//the line below gives value to my parameter, which in this case is a WinForms label
//with the table1/columnB, table2/columnB value (the name that I wish to sort by)
da.SelectCommand.Parameters.Add("#myParameter", SqlDbType.NVarChar).Value = label.Text.ToString();
da.Fill(ds);
}
return ds;
}
Populating my combobox (code is in form load event):
try
{
DataSet ds2 = MyDataSet();
myComboBox.DataSource = ds2.Tables[0];
myComboBox.DisplayMember = "columnC";
myComboBox.ValueMember = "columnA"; // hidden ID row
}
catch (Exception ex)
{
// messageboxcode
}
Oh, and, if it matters, all my columns are nvarchar except for table1/columnA, which is int.
Hope I make sense and that someone is able to give me a hint or two.
In regards to what I've tried to fix this, being new to SQL, I have read documentation, watched tutorials and generally spent hours trying to figure this out. I just can't for some reason! :-)
I can say that your stored procedure is malformed because you don't have a length on nvarchar(). Never use char and related types in SQL Server without a length parameter.. The default varies by context and assuming the default does what you want just leads to hard to debug errors (ahem).
In the absence of other information, you can use (max), but I would suggest that you use an appropriate value for the length of the parameter:
CREATE PROCEDURE [dbo].myProcedure (
#myParameter nvarchar(max)
) AS
BEGIN
SELECT columnA, columnB, columnC
FROM table1 JOIN
table2
ON table1.columnB = table2.columnB
WHERE table1.columnB = #myParameter;
END;
Add the order like:
order by table1.columnB,table2.columnB

Select all columns except the first column for any given SQL Server table

I have this code in C#, but I need it to select all columns EXCEPT the first column of the table (the identity column), so that when I insert the data into an identical table in a different database, the destination database assigns its own identity column values:
SqlCommand commandSourceData = new SqlCommand($"SELECT * FROM dbo.{tableName};", sourceConnection);
SqlDataReader reader = commandSourceData.ExecuteReader();
Is there a way to do this?
If you want a generic solution for every column in your database you can use this kind of code
public string GetColumnsWithoutIdentity(string tableName, SqlConnection con)
{
SqlDataAdapter da = new SqlDataAdapter($"SELECT * FROM dbo.{tableName} where 1=0", con);
DataTable dt = new DataTable();
da.FillSchema(dt, SchemaType.Source);
var cols = dt.Columns.Cast<DataColumn>().Where(x => !x.AutoIncrement).Select(x => x.ColumnName);
return string.Join(",", cols);
}
Now you can use the returned string to build an Sql statement without the autoincrement column.
Notice that this code is vulnerable to Sql Injection. You should be absolutely sure that the tableName parameter used to build the first query is not typed directly by your user. Let it choose from a whitelist (readonly) of predefined tables (and also this is not 100% safe)
Another drawback is the fact that you need to hit the database two times. Once to get the schema with the info about the AutoIncrement column and one to fill the datatable after that.

Displaying data from SQL database in C# WinForm controls (textBox, comboBox, label)

I have a performance problem with displaying data from an SQL database in my application. The problem is that I have a large number of parameters that I need to display (customers personal data, his current statistics etc.).
So far I've used either SqlCommand.ExecuteScalar (for single parameters), or DataTable.Rows[].ItemArray.GetValue() (for multiple parameters - I fill the DataTable with SqlDataAdapter whose query withdraws the necessary data from the database) and assigned their values to the appropriate control. Assuming that command is an SqlCommand type:
For single parameter
command.CommandText = "SELECT Parameter1 FROM MyTable WHERE Condition = Value";
textBox1.Text = command.ExecuteScalar().ToString();
For multiple parameters (SDA is a SqlDataAdapter):
command.CommandText="SELECT Parameter1 - ParameterN FROM MyTable WHERE Condition = Value";
SDA.SelectCommand = command;
SDA.Fill(MyDataTable);
textBox1.Text = MyDataTable.Rows[0].ItemArray.GetValue(0).ToString();
comboBox1.Text = MyDataTable.Rows[0].ItemArray.GetValue(1).ToString();
/*
I repeat similar lines of code for each parameter and display it in the appropriate control.
*/
This approach works correctly but when I have a large number of parameters (20+), it works very slowly.
Is there a more efficient way to display these amounts of data, and how would I implement it?
Thank you
Probably, with the second example, a SqlDataReader will perform better because you read the values just one time, while with a DataAdapter, you need to load the DataTable and then loop over the rows of the table (Effectively reading data two times).
command.CommandText="SELECT Field1,...,FieldN FROM MyTable WHERE Condition = Value";
SqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
// Of course this works correctly just if your query returns one row....
textBox1.Text = reader.GetString(0);
comboBox1.Text = reader.GetString(n);
}
You could also try with the Field<T> extension for the DataRow
command.CommandText="SELECT Field1,...,FieldN FROM MyTable WHERE Condition = Value";
SqlDataAdapter SDA = new SqlDataAdapter(command);
SDA.Fill(MyDataTable);
textBox1.Text = MyDataTable.Rows[0].Field<string>("Field1");
comboBox1.Text = MyDataTable.Rows[0].Field<string>("FieldN");
However, I think that the real performance gain would be in the query that you submit to the database engine and in the correct working of indexes on your tables.
Try to retrieve the minimun number of rows possible, search on indexed fields and/or change to a stored procedure.
here i had write sample stored procedure in wich you can get idea...
you can pass as amny parameter as you can in xml format and insert into temp table...
now you have table with value Name/value pair means Paramater name /value....
now you can do your furteher work...
/*
EXEC wa_TempGetDaya '<SampleXML>
<tblXML><AccountID>3</AccountID><Code>11</Code><Description>Leptospiral infect NEC</Description></tblXML>
</SampleXML>'
*/
CREATE PROCEDURE wa_TempGetDaya
(
#ParaXML NVARCHAR(MAX)
)
AS
SET NOCOUNT ON
BEGIN
DECLARE #AccountID INT
DECLARE #MyXML XML
SET #MyXML = #ParaXML
IF OBJECT_ID('tempdb..#TempData') IS NOT NULL
DROP TABLE #TempData
SELECT * INTO #TempData
FROM (
SELECT
Parse.value('(AccountID)[1]', 'INT') AS 'AccountID',
Parse.value('(Code)[1]', 'Varchar(100)') AS 'Code',
Parse.value('(Description)[1]', 'varchar(1000)') AS 'Description'
FROM
#MyXML.nodes('/SampleXML/tblXML') AS YourData(Parse)
) AS tbl
declare #para1 varchar(20)
declare #para2 varchar(20)
declare #para3 varchar(20)
SELECT #para1 =AccountID ,#para2 =Code,#para3 =Description from #TempICD
END

Using OleDbDataAdapter to update a DataTable C#

I have been trying to use OleDbDataAdapter to update a DataTable but got confused about the commands.
Since I sometimes get info from diffrent tables I can't use a CommandBuilder.
So I have tried to create the commands on my on but found it hard with the parameters.
DataTable.GetChanges returns rows that needs to use an INSERT or an UPDATE command - I guess I can't distinct between them.
I need you to complete the following:
DataTable dt = new DataTable();
OleDbDataAdapter da = new OleDbDataAdapter();
// Here I create the SELECT command and pass the connection.
da.Fill(dt);
// Here I make changes (INSERT/UPDATE) to the DataTable (by a DataGridView).
da.UpdateCommand = new OleDbCommand("UPDATE TABLE_NAME SET (COL1, COL2, ...) VALUES (#newVal1, #newVal2, ...) WHERE id=#id"); // How can I use the values of the current row (that the da is updating) as the parameters (#newVal1, #newVal2, id....)?
Thank you very much!
The data adapter can work in conjunction with the datatable. As such, I've actually wrapped mine together into a class and works quite well. Aside from the complexities of my stuff, here's a snippet that might help you along. When adding a parameter, you can identify the column source that the data is coming from FROM the DataTable. This way, when a record is internally identified as "Added" or "Updated" (or "Deleted"), when you build your SQL Insert/Update/Delete commands, it will pull the data from the columns from the respective rows.
For example. Say I have a DataTable, primary Key is "MyID" and has columns "ColX, ColY, ColZ". I create my DataAdapter and build out my select, update, delete commands something like... (? is a place-holder for the parameters)
DataAdapter myAdapter = new DataAdapter()
myAdapter.SelectCommand = new OleDbCommand();
myAdapter.InsertCommand = new OleDbCommand();
myAdapter.UpdateCommand = new OleDbCommand();
myAdapter.DeleteCommand = new OleDbCommand();
myAdapter.SelectCommand.CommandText = "select * from MyTable where MyID = ?";
myAdapter.InsertCommand.CommandText = "insert into MyTable ( ColX, ColY, ColZ ) values ( ?, ?, ? )";
myAdapter.UpdateCommand.CommandText = "update MyTable set ColX = ?, ColY = ?, ColZ = ? where MyID = ?";
myAdapter.DeleteCommand.CommandText = "delete from MyTable where MyID = ?";
Now, each has to have their respective "Parameters". The parameters have to be addded in the same sequence as their corresponding "?" place-holders.
// Although I'm putting in bogus values for preparing the parameters, its just for
// data type purposes. It does get changed through the data adapter when it applies the changes
OleDbParameter oParm = new OleDbParameter( "myID", -1 );
oParm.DbType = DbType.Int32;
oParm.SourceColumn = "myID"; // <- this is where it looks back to source table's column
oParm.ParameterName = "myID"; // just for consistency / readability reference
myAdapter.SelectCommand.Parameters.Add( oParm );
do similar for rest of parameters based on their types... char, int, double, whatever
Again, I have like a wrapper class that handles managment on a per-table basis... in brief
public myClassWrapper
{
protected DataTable myTable;
protected DataAdapter myAdapter;
... more ...
protected void SaveChanges()
{
}
}
Its more complex than just this, but during the "SaveChanges", The datatable and dataAdapter are in synch for their own purposes. Now, flushing the data. I check for the status of the table and then you can pass the entire table to the dataAdapter for update and it will cycle through all changed records and push respective changes. You'll have to trap for whatever possible data errors though.
myAdapter.Update( this.MyTable );
As it finds each "changed" record, it pulls the values from the Column Source as identified by the parameter that is found in the table being passed to the adapter for processing.
Hopefully this has given you a huge jump on what you are running into.
---- COMMENT PER FEEDBACK ----
I would put your update within a try/catch, and step into the program to see what the exception is. The message adn/or inner exception of the error might give more info. However, try to simplify your UPDATE to only include a FEW fields with the WHERE "Key" element.
Additionally, and I oopsed, missed this from first part answer. You might have to identify the datatable's "PrimaryKey" column. To do so, its a property of the DataTable that expects and array of columns that represent the primary key for the table. What I did was...
// set the primary key column of the table
DataColumn[] oCols = { myDataTbl.Columns["myID"] };
myDataTbl.PrimaryKey = oCols;
I would comment out your full update string and all its parameters for your UPDATE. Then, build it with just as simple as my sample of only setting 2-3 columns and the where clause
myAdapter.UpdateCommand.CommandText = "update MyTable set ColX = ?, ColY = ? where MyID=?";
Add Parameter object for "X"
Add Parameter object for "Y"
Add Parameter object for "MyID"
Pick fields like int or char so they have the least probability of problems for data type conversions, then, once that works, try adding all your "int" and "character" columns... then add any others. Also, which database are you going against. SOME databases don't use "?" as placeholder in the command but use "named" parameters, some using
"actualColumn = #namedCol"
or even
"actualColumn = :namedCol"
Hope this gets you over the hump...
You could use the String.Format Method to replace the #newVal1, #newVal2, ... in your code, like this da.UpdateCommand = new OleDbCommand(String.Format("UPDATE TABLE_NAME SET (COL1, COL2, ...) VALUES ({0}, {1}, ...) WHERE id=#id",OBJECT_ARRAY_CONTAINING_VALUES_FROM_THEDG));
[Eidt per comment]
To handle the row[0], row[1] you need a loop like:
for(i=0; i<rows.Count; i++)
{
da.UpdateCommand = new OleDbCommand(String.Format("UPDATE...",row[i]);
da.Update(dt);
}

Using temporary table in c#

I read an excel sheet into a datagrid.From there , I have managed to read the grid's rows into a DataTable object.The DataTable object has data because when I make equal a grid's datasource to that table object , the grid is populated.
My Problem : I want to use the table object and manipulate its values using SQL server,(i.e. I want to store it as a temporary table and manipulate it using SQL queries from within C# code and , I want it to return a different result inte a grid.(I don't know how to work with temporary tables in C#)
Here's code to execute when clicking button....
SqlConnection conn = new SqlConnection("server = localhost;integrated security = SSPI");
//is connection string incorrect?
SqlCommand cmd = new SqlCommand();
//!!The method ConvertFPSheetDataTable Returns a DataTable object//
cmd.Parameters.AddWithValue("#table",ConvertFPSheetDataTable(12,false,fpSpread2_Sheet1));
//I am trying to create temporary table
//Here , I do a query
cmd.CommandText = "Select col1,col2,SUM(col7) From #table group by col1,col2 Drop #table";
SqlDataAdapter da = new SqlDataAdapter(cmd.CommandText,conn);
DataTable dt = new DataTable();
da.Fill(dt); ***// I get an error here 'Invalid object name '#table'.'***
fpDataSet_Sheet1.DataSource = dt;
//**NOTE:** fpDataSet_Sheet1 is the grid control
Change your temp table from #table to ##table in both places.
Using ## means a global temp table that stays around. You'll need to Drop it after you have completed your task.
Command = " Drop Table ##table"
Putting the data into a database will take time - since you already have it in memory, perhaps LINQ-to-Objects (with DataSetExtensions) is your friend? Replace <int> etc with the correct types...
var query = from row in table.Rows.Cast<DataRow>()
group row by new
{
Col1 = row.Field<int>(1),
Col2 = row.Field<int>(2)
} into grp
select new
{
Col1 = grp.Key.Col1,
Col2 = grp.Key.Col2,
SumCol7 = grp.Sum(x => x.Field<int>(7))
};
foreach (var item in query)
{
Console.WriteLine("{0},{1}: {2}",
item.Col1, item.Col2, item.SumCol7);
}
I don't think you can make a temp table in SQL the way you are thinking, since it only exists within the scope of the query/stored procedure that creates it.
If the spreadsheet is a standard format - meaning you know the columns and they are always the same, you would want to create a Table in SQL to put this file into. There is a very fast way to do this called SqlBulkCopy
// Load the reports in bulk
SqlBulkCopy bulkCopy = new SqlBulkCopy(connectionString);
// Map the columns
foreach(DataColumn col in dataTable.Columns)
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
bulkCopy.DestinationTableName = "SQLTempTable";
bulkCopy.WriteToServer(dataTable);
But, if I'm understanding your problem correctly, you don't need to use SQL server to modify the data in the DataTable. You c an use the JET engine to grab the data for you.
// For CSV
connStr = string.Format("Provider=Microsoft.JET.OLEDB.4.0;Data Source={0};Extended Properties='Text;HDR=Yes;FMT=Delimited;IMEX=1'", Folder);
cmdStr = string.Format("SELECT * FROM [{0}]", FileName);
// For XLS
connStr = string.Format("Provider=Microsoft.JET.OLEDB.4.0;Data Source={0}{1};Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'", Folder, FileName);
cmdStr = "select * from [Sheet1$]";
OleDbConnection oConn = new OleDbConnection(connStr);
OleDbCommand cmd = new OleDbCommand(cmdStr, oConn);
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
oConn.Open();
da.Fill(dataTable);
oConn.Close();
Also, in your code you ask if your connection string is correct. I don't think it is (but I could be wrong). If yours isn't working try this.
connectionString="Data Source=localhost\<instance>;database=<yourDataBase>;Integrated Security=SSPI" providerName="System.Data.SqlClient"
Pardon me, if I have not understood what you exactly want.
If you want to perform SQL query on excel sheet, you could do it directly.
Alternatively, you can use SQL Server to query excel (OPENROWSET or a function which I dont remember right away). Using this, you can join a sql server table with excel sheet
Marc's suggestion is one more way to look at it.
Perhaps you could use a DataView. You create that from a DataTable, which you already have.
dv = new DataView(dataTableName);
Then, you can filter (apply a SQL WHERE clause) or sort the data using the DataView's methods. You can also use Find to find a matching row, or FindRows to find all matching rows.
Some filters:
dv.RowFilter = "Country = 'USA'";
dv.RowFilter = "EmployeeID >5 AND Birthdate < #1/31/82#"
dv.RowFilter = "Description LIKE '*product*'"
dv.RowFilter = "employeeID IN (2,4,5)"
Sorting:
dv.Sort = "City"
Finding a row: Find the customer named "John Smith".
vals(0)= "John"
vals(1) = "Smith"
i = dv.Find(vals)
where i is the index of the row containing the customer.
Once you've applied these to the DataView, you can bind your grid to the DataView.
Change the command text from
Select col1,col2,SUM(col7) From #table group by col1,col2
to
Select col1,col2,SUM(col7) From ##table group by col1,col2

Categories

Resources