I'm working with TSQL and C#. I have two queries that return strings:
string[] allSubcategories = dt.AsEnumerable().Select(x => x.Field<string>("SubcategoryName")).Distinct().ToArray();
var redMark = db.GetTableBySQL("SELECT * FROM RedMarkItems");
string[] redMarkColumns = redMark.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray();
So, as you can see I have two different arrays, first I get subcategoriesNames:
and all columns of table RedMarkItems:
That I want to do is to create column dynamically, I mean, if subcategorieName does not exist as column in RedMarkItems do an Update and create it someting like:
var createColumn = db.ExeSQL($"ALTER TABLE RedMarkItems ADD {ColumnName} BIT");
How can I compare if subcategorieName does not exist as column in RedMarkItems table? Then create column as my query? Regards
If you want to know if a particular column exists in an already filled DataTable using the Linq approach then it is just:
bool exists = redMark.Columns.Cast<DataColumn>().Any(x => x.ColumnName == "SubCategoryName");
Instead, if you want to ask this info directly to the database then use the INFORMATION_SCHEMA views The Columns view is the one to use with a query like this.
string query = #"IF EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.Column
WHERE Column_Name = #colName)
SELECT 1 ELSE SELECT 0";
SqlCommand cmd = new SqlCommand(query, connection);
cmd.Parameters.Add("#colName", SqlDbType.NVarChar).Value = "SubCategoryName";
bool exists = (cmd.ExecuteScalar() == 1);
Now, the part about creating the column is pretty simple as code per se. It is just an appropriate ALTER TABLE. But there are a lot of things to be cleared before. What will be the datatype of the new column? What will be its length and precision? What will be the constraints applied to it (Null/Not Null defaults etc)? As you can see all these info are very important and require to be defined somewhere in your code.
Related
I have a project in C# which the user can choose only one row in datagridview and fill it's data by filling textboxes, but that takes a long time to do, so how can I develop the below code that user can choose a group of names (which the same data should be recorded) by select several rows in datagridview to insert data into SQL server database?
this the code I use after select a separate row:
{
hour.ADD_ADDITIONAL_HOURS(Convert.ToInt32(txtRecID.Text), dtDateFrom.Value.Date, dgActiveEmps.CurrentRow.Cells[0].Value.ToString(), Convert.ToInt32(cmbSymboles.SelectedValue), Convert.ToInt32(txtHours.Text));
}
note: ADD_ADDITIONAL_HOURS is a stored procedure
The easiest way to do this is to use table valued parameters. To do this, firstly create a user defined table type in SQL Server thus:
CREATE TYPE dbo.YourTypeName AS TABLE
(
[ID] [nvarchar(50)]
)
(I am assuming that your Cells[0].value contains a string. If it is another data type, then change the type of ID above appropriately).
Next change the third parameter of your stored procedure to <parameter_name> AS dbo.YourTypeName READONLY). Within the body of your procedure, change the update(?) statement to be based on a join with this parameter - you use it like any other table. It should look something like:
UPDATE t
SET col1 = #param1 ....
FROM YourTable t INNER JOIN #tablevaluedparameter tvp ON tvp.ID = t.ID
Turning to c#, you need to change the third parameter passed to the stored procedure to be SqlDbType.Structured. Finally you need to populate the parameter with the values from SelectedRows. Here you have a choice: the values can be placed in any of DataTable, IEnumerable<SqlDataRecord> or DbDataReader; all of these can be the Value property of the parameter.
For example, using a DataTable your code would look something like this:
var dT = new DataTable();
dT.Columns.Add(new DataColumn("ID", typeof(string));
for (int i = 0: i < dgActiveEmps.SelectedRows.Count; i++)
{
dT.Rows.Add(dgActiveEmps.SelectedRows[i].Cells[0].Value.ToString());
}
yourSqlCommand.Parameters.Add(new SqlParameter
{
ParameterName = "#tablevaluedparameter",
SqlDbType = SqlDbType.Structured,
Value = dT,
TypeName = "dbo.YourTypeName"
});
Obviously you will need to adapt the namings to fit your case, but I hope it should be clear what you need to do.
I have got a single function using OleDb:
I think maybe the use of a list to find out if a specific table exist could be made better, like this:
Check for MS Access database table if not exist create it
But I also want to understand the old code given, to learn something.
Questions:
1) What does the exact restrictionValues mean in the example code below? ( not solved)
2) Why row.ItemArray[2] of all cells is containing the table names? (solved)
3) Is there a better way to get table names out of a database? (solved)
This is the code I have got:
public List<string> GetTableNames(string tableName, string[] field_names)
{
List<string> retTableNames = new List<string>();
if (dbConnection != null)
{
dbConnection.Open();
string strSQL = "SELECT * ";
string[] restrictionValues = new string[4] { null, null, null, "TABLE" };
OleDbCommand cmd = new OleDbCommand(strSQL, dbConnection);
try
{
/*search after all possible tables in dataset*/
DataTable schemaInformation = dbConnection.GetSchema("Tables", restrictionValues);
foreach (DataRow row in schemaInformation.Rows)
{
retTableNames.Add(row.ItemArray[2].ToString());
}
}
catch
{
retTableNames = null;
}
}
return retTableNames;
}
(I just noticed that you said you already get this first part, but I'll leave it in anyway. There's a little more below about "TABLE".)
Some of the explanation of the restrictions is in the Main program in the link that #jdweng gave.
// You can specify the Catalog, Schema, Table Name, Table Type to get
// the specified table(s).
// You can use four restrictions for Table, so you should create a 4 members array.
String[] tableRestrictions = new String[4];
// For the array, 0-member represents Catalog; 1-member represents Schema;
// 2-member represents Table Name; 3-member represents Table Type.
// Now we specify the Table Name of the table what we want to get schema information.
tableRestrictions[2] = "Course";
DataTable courseTableSchemaTable = conn.GetSchema("Tables", tableRestrictions);
The rest of the explanation is in the overload of GetSchema: GetSchema Method (String, String[]), which is what you're using.
Verbatim:
In order to set values on a given restriction, and not set the values of other restrictions, you need to set the preceding restrictions to null and then put the appropriate value in for the restriction that you would like to specify a value for.
An example of this is the "Tables" collection. If the "Tables" collection has three restrictions -- database, owner, and table name--and you want to get back only the tables associated with the owner "Carl", you need to pass in the following values: null, "Carl". If a restriction value is not passed in, the default values are used for that restriction. This is the same mapping as passing in null, which is different from passing in an empty string for the parameter value. In that case, the empty string ("") is considered to be the value for the specified parameter.
A more complete article on schema restrictions.
It looks like you can omit some parameters, which is why the example above only lists 3 restrictions.
I've been looking for an explanations of the "TABLE" parameter but it's hard to find. It's either a default that gets all tables, or it's ignored, or something else. The easiest way to get all table types might be to do a basic DataTable table = connection.GetSchema("Tables"); then get the types of each table to see what the options are. Otherwise, sticking to "TABLE" will no doubt get the commonly used tables, not system tables or anything like that.
Hope your ears are ok.
I did a search and found some seemingly related answers, but they don't really do what I'm after.
Given a valid connection string and a table name, I want to get a DataTable of the table. I.e. if the table has a column called "Name", I want the DataTable set up so I can do dt["Name"] = "blah";
The trick is, I don't want to hard code any of that stuff, I want to do it dynamically.
People tell you to use SqlConnection.GetSchema, but that gives you back a table with a bunch of stuff in it.
Everybody has random tricks like TOP 0 * from the table and get the schema from there, etc.
But is there a way to get the table with the primary keys, unique indexes, etc. Ie.. in the final format to do a bulk insert.
You can use SqlDataAdapter.FillSchema:
var connection = #"Your connection string";
var command = "SELECT * FROM Table1";
var dataAdapter = new System.Data.SqlClient.SqlDataAdapter(command, connection);
var dataTable = new DataTable();
dataAdapter.FillSchema(dataTable, SchemaType.Mapped);
This way you will have an empty DataTable with columns and keys defined and ready to use in code like dataTable["Name"] = "blah";.
I have an array or string:
private static string[] dataNames = new string[] {"value1", "value2".... };
I have table in my SQL database with a column of varchar type. I want to check which values from the array of string exists in that column.
I tried this:
public static void testProducts() {
string query = "select * from my table"
var dataTable = from row in dt.AsEnumerable()
where String.Equals(row.Field<string>("columnName"), dataNames[0], StringComparison.OrdinalIgnoreCase)
select new {
Name = row.Field<string> ("columnName")
};
foreach(var oneName in dataTable){
Console.WriteLine(oneName.Name);
}
}
that code is not the actual code, I am just trying to show you the important part
That code as you see check according to dataNames[index]
It works fine, but I have to run that code 56 times because the array has 56 elements and in each time I change the index
is there a faster way please?
the Comparison is case insensitive
First, you should not filter records in memory but in the datatabase.
But if you already have a DataTable and you need to find rows where one of it's fields is in your string[], you can use Linq-To-DataTable.
For example Enumerable.Contains:
var matchingRows = dt.AsEnumerable()
.Where(row => dataNames.Contains(row.Field<string>("columnName"), StringComparer.OrdinalIgnoreCase));
foreach(DataRow row in matchingRows)
Console.WriteLine(row.Field<string>("columnName"));
Here is a more efficient (but less readable) approach using Enumerable.Join:
var matchingRows = dt.AsEnumerable().Join(dataNames,
row => row.Field<string>("columnName"),
name => name,
(row, name) => row,
StringComparer.OrdinalIgnoreCase);
try to use contains should return all value that you need
var data = from row in dt.AsEnumerable()
where dataNames.Contains(row.Field<string>("columnName"))
select new
{
Name = row.Field<string>("columnName")
};
Passing a list of values is surprisingly difficult. Passing a table-valued parameter requires creating a T-SQL data type on the server. You can pass an XML document containing the parameters and decode that using SQL Server's convoluted XML syntax.
Below is a relatively simple alternative that works for up to a thousand values. The goal is to to build an in query:
select col1 from YourTable where col1 in ('val1', 'val2', ...)
In C#, you should probably use parameters:
select col1 from YourTable where col1 in (#par1, #par2, ...)
Which you can pass like:
var com = yourConnection.CreateCommand();
com.CommandText = #"select col1 from YourTable where col1 in (";
for (var i=0; i< dataNames.Length; i++)
{
var parName = string.Format("par{0}", i+1);
com.Parameters.AddWithValue(parName, dataNames[i]);
com.CommandText += parName;
if (i+1 != dataNames.Length)
com.CommandText += ", ";
}
com.CommandText += ");";
var existingValues = new List<string>();
using (var reader = com.ExecuteReader())
{
while (read.Read())
existingValues.Add(read["col1"]);
}
Given the complexity of this solution I'd go for Max' or Tim's answer. You could consider this answer if the table is very large and you can't copy it into memory.
Sorry I don't have a lot of relevant code here, but I did a similar thing quite some time ago, so I will try to explain.
Essentially I had a long list of item IDs that I needed to return to the client, which then told the server which ones it wanted loaded at any particular time. The original query passed the values as a comma separated set of strings (they were actually GUIDs). Problem was that once the number of entries hit 100, there was a noticeable lag to the user, once it got to 1000 possible entries, the query took a minute and a half, and when we went to 10,000, lets just say you could boil the kettle and drink your tea/coffee before it came back.
The answer was to stick the values to check directly into a temporary table, where one row of the table represented one value to check against. The temporary table was keyed against the user who performed the search, so this meant other users searches wouldn't become corrupted with each other, and when the user logged out, then we knew which values in the search table could be removed.
Depending on where this data comes from will depend on the best way for you to load the reference table. But once it is there, then your new query will look something like:-
SELECT Count(t.*), rt.dataName
FROM table t
RIGHT JOIN referenceTable rt ON tr.dataName = t.columnName
WHERE rt.userRef = #UserIdValue
GROUP BY tr.dataName
The RIGHT JOIN here should give you a value for each of your reference table values, including 0 if the value did not appear in your table. If you don't care which one don't appear, then changing it to an INNER JOIN will eliminate the zeros.
The WHERE clause is to ensure that your search only returns the unique items that you are looking for at the moment - the design should consider that concurrent access will someday occur here (even if it doesn't at the moment), so writing something in to protect it is advisable.
I am using EntityFramework v6.1 with MySql.Data.Entity.EF6 v 6.8.3.0.
I am attempting to get the "Headers" or "Column Names" for data inside tables. I have a table called "dbases" and I need to get the Column Names from within ADO.NET, please help!
using (var connection = new hyperion_collectionsmaxEntities())
{
var portfolios = connection.portfolios.ToList();
portfolios.ForEach(o => comboBox1.Items.Add(o.portfolio1));
var statuses = connection.adminstatus.ToList();
statuses.ForEach(o => chkLstBoxStatuses.Items.Add(o.statusname));
//var headers = connection.dbases ?? <~~~
}
One way to get at the column names is to query to MetaTables:
SELECT COLUMN_NAME, TABLE_NAME
FROM information_schema.COLUMNS
WHERE information_schema.COLUMNS.table_schema = 'dbases';
There are a lot of interesting data there. For simply getting at the Column Name and the (default) Header you can use the DataTable.Column's properties:
yourTable.Columns[columnIndex].ColumnName
yourTable.Columns[columnIndex].Caption
If EF has special ways I don't know about them.. but in the end imo the data should go into the DataTable..