I have a program in c# (asp.net environment) that dynamically runs ORACLE queries.
the queries can be like:
select * from app_costumers;
select a.first_name, a.last name, b.salary from app_costumers inner join app_costs b on a.id = b.id;
(As you can see, this is sometimes about a single table and sometimes more.
Sometimes writing the column names and sometimes using only in "*").
until now, I returned only the results,
But now I need to return the names of the results columns.
Do you have any idea how to do this?
(I can't use something like this:
SELECT column_name FROM user_tab_cols WHERE table_name = UPPER ('app_costumers');
Because it only fits for one table ...).
Thank you
You can use reader.GetName(i), for example:
var reader = cmd.ExecuteReader();
var columns = new List<string>();
for(int i=0;i<reader.FieldCount;i++)
{
columns.Add(reader.GetName(i));
}
Related
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.
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..
I have two tables. Most of the data is coming from the first table but there is a second table which has a column which I want to present in my UI
Here is my SQL Query
String sqlQuery = "SELECT u.CallerName, t.* FROM users u INNER JOIN tickets t ON u.id = t.user WHERE u.CallerName = 'tim.smith'";
I am using WinForms
If your query result is DataTable, then you can use Merge function to merge two table.
DataTable table1 = GetTable1Data(...);
DataTable table2 = GetTable2Data(...);
table1.Merge(table2, true);
Or if your query result is List, then you can use same approach as in DataTable case, using AddRange function:
List<YourClassType> list1 = GetList1Data(...);
List<YourClassType> list2 = GetList2Data(...);
list1.AddRange(list2, true);
It looks like you are doing just fine.
When binding with the DataGridView you can use:
Eval("CallerName")
to access the other column, but that column should work like all of the others.
I have two tables I am using to fill a gridview. The tables have a common field named RangeActivityID. My problem is that the database is very old and some of the older entries do not match up IDs between tables, so I am unable to add an association between them in the database.
I do not care about the old data which doesn't match up, so in my .dbml file, I manually created an association in order to select good data from both tables. This is the LINQ query I have:
var collective = from p in rangeConnection.RangeActivities
orderby p.RangeActivityID
select new
{
TestName = p.TestName,
DateTime = p.ActivityDateTime,
Notes = p.Notes,
//RoundSerialNumber = p.RoundFire.RoundSerialNumber,
//RoundType = p.RoundFire.RoundType,
//LotStockNumber = p.RoundFire.LotNumber
};
I can set my grid datasource to 'collective' and everything works, but if I uncomment the three commented lines, the query returns no results because the tables have data that doesn't meet the association criteria. Is there a way to have the LINQ query ignore results that do not match up?
Thanks in advance!
Try to add where p.RoundFire != null criteria.
Suggest a join instead, and emulating a SQL LEFT JOIN.
var q = from p in rangeConnection.RangeActivities
join r in rangeConnection.RoundFires
on p.RangeActivityID equals r.RangeActivityID into sr
from x in sr.DefaultIfEmpty()
select new
{
TestName = p.TestName,
DateTime = p.ActivityDateTime,
Notes = p.Notes,
RoundSerialNumber = x.RoundSerialNumber,
RoundType = x.RoundType,
LotStockNumber = x.LotNumber
//consider checking for string.IsNullOrEmpty()
//for the RoundFires properties
};
The syntax for your entities may be inaccurate, but please edit my answer if it helps lead you to a solution.