Is there a way to determine a column's defaultvalue from the Sql Database using ado.net?
I tried using the SqlDataAdapter's FillSchema method:
using (SqlDataAdapter adapter = new SqlDataAdapter()) {
adapter.SelectCommand = myConnection.CreateCommand();
adapter.SelectCommand.CommandType = CommandType.Text;
adapter.SelectCommand.CommandText = "SELECT * FROM myTable";
DataTable table = new DataTable();
adapter.Fill(table);
adapter.FillSchema(table, SchemaType.Mapped);
}
When I inspect the DataColumns in the DataTable, I can determine if a column is an AutoIncrement, and can determine if it allows nulls using the AllowDBNull property. However, DefaultValue (for columns that I know have a default value) is always null.
I considered:
DataTable schemaTable = null;
using (SqlDataReader reader = adapter.SelectCommand.ExecuteReader(CommandBehavior.SchemaOnly)) {
schemaTable = reader.GetSchemaTable();
reader.Close();
}
but DefaultValue is not included in the schema.
So...how can I get a column's DefaultValue?
Use this query to interrogate the INFORMATION_SCHEMA for the info you're looking for:
SELECT
TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'your table name' AND
COLUMN_NAME = 'your column name'
Marc
Not really. This is because the default value can be determined at the time a record is inserted (like a GETDATE() or NEW_ID()). So the value cannot be determined in advance.
The COLUMN_DEFAULT column of INFORMATION_SCHEMA.COLUMNS gives you not the actual default value, but a string representation of the code SQL Server and the likes will execute to generate the default value. See http://msdn.microsoft.com/en-us/library/ms188348.aspx.
Having said that, simple constant values can easily be deduced from such an expression.
Related
I want to import a table from Access DB, in this table there is a column that is valued and not, when not I want to fill this column on the result table with a default value or let it empty.
I tried IsNull(col2 ,'')
Thanks in advance
MyQuery = "SELECT col1 AS col1, col2 AS myDevice";
MyQuery += " FROM table 1";
OleDbCommand cmd1 = new OleDbCommand(MyQuery, conn);
OleDbDataAdapter adapter1 = new OleDbDataAdapter(cmd1);
adapter1.Fill(table);
DB.Tables.Add(table);
In MS Access, you want the Nz() function:
Nz(col2 ,'default')
IsNull() exists in Access but it is meant to check if a value is null (and it returns a boolean value).
I solved with this:
MyQuery = "SELECT col1 AS col1, IIF(ISNULL(col2), 0, col2) AS myDevice";
Thanks
I have a SQL Server database which has a lot of information inside.
I want to select top 50 rows in a single query (which I did, with no problem) but then I want to update a column from false to true, so next time I select I wont select the same, my code looks like this:
string Command = "UPDATE HubCommands SET [Alreadytaken] = 'true' FROM (SELECT TOP 50 [CommandId],[DeviceId],[Commandtext], [HashCommand],[UserId] FROM HubCommands) I WHERE [HubId] = '18353fe9-82fd-4ac2-a078-51c199d9072b'";
using (SqlConnection myConnection = new SqlConnection(SqlConnection))
{
using (SqlDataAdapter myDataAdapter = new SqlDataAdapter(Command, myConnection))
{
DataTable dtResult = new DataTable();
myDataAdapter.Fill(dtResult);
foreach (DataRow row in dtResult.Rows)
{
Guid CommandId, DeviceId, UserId;
Guid.TryParse(row["CommandId"].ToString(), out CommandId);
Guid.TryParse(row["DeviceId"].ToString(), out DeviceId);
Guid.TryParse(row["UserId"].ToString(), out UserId);
Console.WriteLine("CommandId" + CommandId);
}
}
}
This code does work, and it updates what I ask it to update, but I don't get nothing in the data table, its like it is always updating but not selecting.
If I do a normal select it does work and give information.
Does anyone have any idea how to update and get some data back, in a single query?
So your question is:
How can I update a table in SQL Server using C# and return the truly updated
rows as a DataTable ?
First You have multiple issues in your query.
You should use 1 and 0, not true or false. SQL-Server has a bit datatype and not a Boolean.
Second, this is how you should've constructed your query:
DECLARE #IDs TABLE
(
[CommandId] uniqueidentifier
);
INSERT INTO #IDs
SELECT [CommandId] FROM HubCommands
WHERE [HubId] = '18353fe9-82fd-4ac2-a078-51c199d9072b' AND [Alreadytaken] = 0;
UPDATE HubCommands
SET [Alreadytaken] = 1
WHERE CommandId IN
(
SELECT [CommandId] FROM #IDs
);
SELECT * FROM HubCommands
WHERE CommandId IN
(
SELECT [CommandId] FROM #IDs
);
Wrap all the above in a single string and use SqlDataReader. No need for an Adapter in you case (Since we're mixing commands unlike what the adapter usually does):
var sqlCommand = new SqlCommand(Command, myConnection);
SqlDataReader dataReader = sqlCommand.ExecuteReader();
DataTable dtResult = new DataTable();
dtResult.Load(dataReader);
I highly advise you to create a stored procedure accepting HubId as a parameter that does all the above work. It is neater and better for maintenance.
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);
}
Below is a snippet of the code. As you can see, that method returns a table from SQLite database, and adds that table to a DataSet if it doesn't exist yet.
SQLiteConnection connection;
DataSet Set = new DataSet();
DataTable GetTable(string tableName, string command)
{
if (!Set.Tables.Contains(tableName))
{
var adapter = new SQLiteDataAdapter(command, connection);
SQLiteCommandBuilder builder = new SQLiteCommandBuilder(adapter);
adapter.FillSchema(Set, SchemaType.Source, tableName);
adapter.Fill(Set, tableName);
adapter.Dispose();
}
return Set.Tables[tableName];
}
To call it, for example
DataTable myTable = GetTable("MyTable", "select * from MyTable);
To access a field:
object emptyValue = myTable.Rows[0]["Some_Column"];
There are some cells in the SQLite file that are of type INT, and their values are empty (not null). However when I'm trying to populate myTable, they are conveniently converted to 0's which I DO NOT WANT. How do I go about fixing that? I would like to keep empty values (and null values) as null's when importing to C#.
You can retrieve the row I was talking about above by executing the following SQL statement:
select * from MyTable where some_column = ''
The SQLite file that I use is SQLite3. Just in case it helps.
Thanks in advance!
I have a stored proc which accepts user defined table type and default values for all the columns in the user defined data type is set to null.
Now i am passing a dataTable with less columns to stored procedure from c# code expecting that the values for remaining columns will be set to null.
But i am getting this error:
Trying to pass a table-valued parameter with 21 column(s) where the corresponding user-defined table type requires 77 column(s).
This is the code
SqlConnection conn = new SqlConnection("server=****; database=***;integrated security=SSPI");
DataSet dataset=new DataSet();
conn.Open();
SqlCommand cmd = new SqlCommand("Insert");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = conn;
SqlParameter para = new SqlParameter();
para.ParameterName = "#TableVar";
para.SqlDbType = SqlDbType.Structured;
//SqlParameter para=cmd.Parameters.AddWithValue("#TableVar",table);
para.Value = table;
cmd.Parameters.Add(para);
cmd.ExecuteNonQuery();
You can use a SqlDataAdapter to create a DataTable matching the table type schema:
DataTable table = new DataTable();
// Declare a variable of the desired table type to produce a result set with it's schema.
SqlDataAdapter adapter = new SqlDataAdapter("DECLARE #tableType dbo.UserDefindTableType
SELECT * FROM #tableType", ConnectionString);
// Sets the DataTable schema to match dbo.UserDefindTableType .
adapter.FillSchema(table, SchemaType.Source);
You can then create DataRows with the all the default column values and just set the columns you know about:
DataRow row = table.NewRow();
// Set know columns...
row["ColumnName"] = new object();
// or check column exists, is expected type etc first
if (table.Columns.Contains("ColumnName")
&& table.Columns["ColumnName"].DataType == typeof(string)) {
row["ColumnName"] = "String";
}
table.Rows.Add(row);
I'm having the same issue and the solution I'm working on is to run an extra SQL query to get the column definition and then fill up the DataTable with the missing columns, here is the SQL statement for a column definition on your table type:
select c.name, t.name as type, c.max_length as length from sys.table_types tt
inner join sys.columns c on c.object_id = tt.type_table_object_id
inner join sys.types t on t.system_type_id = c.system_type_id
where tt.name = #tabletypename
order by c.column_id
The C# code will be a bit more messy as you have to parse the return type (ie VARCHAR, INT, etc) into a SqlDbType enum if you want the solution to work for all table valued parameter defintions..
I would have thought stuff like this could have been better solved by Microsoft inside the SQL Server engine as its the next best way to import CSV files if you do not have write access to the local filesystem, but I want a single UDTT to cater for all CSV files not having to create a new table type every time I deal with a new file format. Anyways.. rant over.