I can use GetSchemaTable and GetXMLSchema to get information about field types, sizes etc. from a Foxpro DBF opened with VFPOLEDB but can not get any information pertaining to what indexes are in the tables/CDX.
I dont want to use the indexes, just the criteria on which the index is built to aid me in generating the SQL commands to create the tables on a SQL server and import the data.
I could do a DISPLAY STRUCTURE output to a text file on all of the tables and parse it in VB.NET but I am hoping there is something I am overlooking as I am not that familiar with VB.NET/OLEDB syntax yet.
Little bit of research given away these results. I have started with having a look at ForeignKey.FKTableSchema Property and unfortunately not a .NET property. Later on things looked good when I found OleDbSchemaGuid.Indexes Field and everything looked good until I ran the application and got the Method is not supported by this provider. Eventually the following article lit up the way,
GetOleDbSchemaTable(OleDb.OleDbSchemaGuid.Indexes - How to access included columns on index
and this finding
The OleDb and Odbc providers do not provide a built-in catalog method
that will return non-key ("Included") index columns.
However it was some really interesting suggestion which let to writing this little Console application for you to harvest the indexes available in the table. This is achieved by directly querying the schema table from SQL. The following example is on Employees table of famous Northwind sample database. Here you go,
//Open a connection to the SQL Server Northwind database.
var connectionString =
"Provider=SQLOLEDB;Data Source=SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=SSPI;Encrypt=False;TrustServerCertificate=False";
using (var connection = new OleDbConnection(connectionString))
{
connection.Open();
var select = "SELECT " +
" T.name AS TABLE_NAME" +
" , IX.name AS INDEX_NAME" +
" , IC.index_column_id AS IX_COL_ID" +
" , C.name AS COLUMN_NAME" +
" , IC.is_included_column AS INCLUDED_NONKEY" +
" " +
"FROM " +
" sys.tables T " +
" INNER JOIN sys.indexes IX" +
" ON T.object_id = IX.object_id " +
" INNER JOIN sys.index_columns IC" +
" ON IX.object_id = IC.object_id " +
" AND IX.index_id = IC.index_id " +
" INNER JOIN sys.columns C" +
" ON IC.object_id = C.object_id " +
" AND IC.column_id = C.column_id " +
" " +
"WHERE T.name = 'Employees'" +
"ORDER BY IC.index_column_id";
OleDbCommand cmd = new OleDbCommand(#select, connection);
cmd.CommandType = CommandType.Text;
var outputTable = new DataSet("Table");
var my = new OleDbDataAdapter(cmd).Fill(outputTable);
foreach (DataTable table in outputTable.Tables)
{
foreach (DataRow myField in table.Rows)
{
//For each property of the field...
foreach (DataColumn myProperty in table.Columns)
{
//Display the field name and value.
Console.WriteLine(myProperty.ColumnName + " = " +
myField[myProperty].ToString());
}
Console.WriteLine();
}
}
}
Console.ReadLine();
and finally the results,
TABLE_NAME = Employees
INDEX_NAME = PK_Employees
IX_COL_ID = 1
COLUMN_NAME = EmployeeID
INCLUDED_NONKEY = False
TABLE_NAME = Employees
INDEX_NAME = LastName
IX_COL_ID = 1
COLUMN_NAME = LastName
INCLUDED_NONKEY = False
TABLE_NAME = Employees
INDEX_NAME = PostalCode
IX_COL_ID = 1
COLUMN_NAME = PostalCode
INCLUDED_NONKEY = False
However, later on by removing the restrictions I managed to get over the Method is not supported by this provider. error and ended up summing it up like this shorter solution,
//Open a connection to the SQL Server Northwind database.
var connectionString =
"Provider=SQLOLEDB;Data Source=SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=SSPI;Encrypt=False;TrustServerCertificate=False";
using (OleDbConnection cnn = new OleDbConnection(connectionString))
{
cnn.Open();
DataTable schemaIndexess = cnn.GetSchema("Indexes",
new string[] {null, null, null});
DataTable schemaIndexes = cnn.GetOleDbSchemaTable(
OleDbSchemaGuid.Indexes,
new object[] {null, null, null});
foreach (DataRow myField in schemaIndexes.Rows)
{
//For each property of the field...
foreach (DataColumn myProperty in schemaIndexes.Columns)
{
//Display the field name and value.
Console.WriteLine(myProperty.ColumnName + " = " +
myField[myProperty].ToString());
}
Console.WriteLine();
}
Console.ReadLine();
}
Which results to a longer output to sort out but part of it would be
TABLE_CATALOG = Northwind
TABLE_SCHEMA = dbo
TABLE_NAME = Employees
INDEX_CATALOG = Northwind
INDEX_SCHEMA = dbo
INDEX_NAME = LastName
PRIMARY_KEY = False
UNIQUE = False
CLUSTERED = False
TYPE = 1
FILL_FACTOR = 0
INITIAL_SIZE =
NULLS =
SORT_BOOKMARKS = False
AUTO_UPDATE = True
NULL_COLLATION = 4
ORDINAL_POSITION = 1
COLUMN_NAME = LastName
COLUMN_GUID =
COLUMN_PROPID =
COLLATION = 1
CARDINALITY =
PAGES = 1
FILTER_CONDITION =
INTEGRATED = False
Perfect!
Everything I needed to extract. Since it was in vb.net section I posted my rough code, I filtered the fields returned so I could list a few here.
It returns all pertinent information relating to the indexes, even complex ones created with expressions.
All tables in the path provided in the Connection String with CDX indexes will be returned.
TABLE_NAME = schematest
INDEX_NAME = char3ascen
NULLS = 1
EXPRESSION = char3ascen
TABLE_NAME = schematest
INDEX_NAME = expressn
NULLS = 2
EXPRESSION = LEFT(char1null,4)+SUBSTR(char2,4,2)
TABLE_NAME = schematest
INDEX_NAME = multifld
NULLS = 2
EXPRESSION = char1null+char2
TABLE_NAME = customer
INDEX_NAME = zip
NULLS = 1
EXPRESSION = zip
Private Sub GetIndexInfo_Click(sender As Object, e As EventArgs) Handles GetIndexInfo.Click
Dim cnnOLEDB As New OleDbConnection
Dim SchemaTable As DataTable
Dim myField As DataRow
Dim myProperty As DataColumn
Dim ColumnNames As New List(Of String)
Dim strConnectionString = "Provider=vfpoledb;Data Source=D:\ACW\;Collating Sequence=general;DELETED=False"
cnnOLEDB.ConnectionString = strConnectionString
cnnOLEDB.Open()
ColumnNames.Add("TABLE_NAME")
columnnames.Add("INDEX_NAME")
columnnames.Add("NULLS")
columnnames.Add("TYPE")
columnnames.Add("EXPRESSION")
SchemaTable = cnnOLEDB.GetSchema("Indexes")
'For Each myProperty In SchemaTable.Columns
For Each myField In SchemaTable.Rows
For Each myProperty In SchemaTable.Columns
If ColumnNames.Contains(myProperty.ColumnName) Then
Console.WriteLine(myProperty.ColumnName & " = " & myField(myProperty).ToString)
End If
Next
Console.WriteLine()
Next
Console.ReadLine()
DGVSchema.DataSource = SchemaTable
End Sub
Related
This code returns the following error:
"System.Data.SqlClient.SqlException (0x80131904): Invalid column name 'a51'"
a51 is the correct value inside of the record I'm looking for in the EstablishmentCode column of the Establishments table. Account ID is used to find all entries on the Establishments table with that account ID and populate a dataset with Establishment Code values. Account ID value comes from a session variable. Then I use each of these values in a loop where each iteration calls a datareader while loop. Hope I explained this clearly, but I would gladly clarify more if needed. Here's my code.
myConnection.Open();
SqlCommand getEst = new SqlCommand("SELECT EstablishmentCode FROM Establishments WHERE AccountID = " + ID, myConnection);
da = new SqlDataAdapter(getEst);
ds = new DataSet();
da.Fill(ds);
int maxrows = ds.Tables[0].Rows.Count;
for (int x = 0; x < maxrows; x++)
{
getPhones = new SqlCommand("SELECT * FROM DispatcherPhones WHERE EstablishmentCode = " + ds.Tables[0].Rows[x].ItemArray.GetValue(0).ToString(), myConnection);
myReader = getPhones.ExecuteReader();
while (myReader.Read())
{
Response.Write("<section id='phone" + myReader["Phone"].ToString() + "' style='padding:20px'>");
Response.Write("<section>Phone Number<br><div class='phone'>" + myReader["Phone"].ToString() + "</div></section>");
Response.Write("<section>Location Code<br><div class='name'>" + myReader["EstablishmentCode"].ToString() + "</div></section>");
Response.Write("<section>Active<br><div class='name'>" + myReader["Active"].ToString() + "</div></section>");
Response.Write("<section class='flex phoneButtonSection'>");
Response.Write("<button type=\"button\" onclick=\"showPhoneForm('" + myReader["ID"].ToString() + "');\">CHANGE</button>");
Response.Write("<button type=\"button\" onclick=\"deletePhones('" + myReader["ID"].ToString() + "');\">DELETE</button>");
Response.Write("</section>");
Response.Write("</section>");
}
myReader.Close();
}
myReader.Close();
myConnection.Close();
String literals in SQL are denoted by single quotes ('s) which are missing for your value:
getPhones = new SqlCommand
("SELECT * " +
"FROM DispatcherPhones
"WHERE EstablishmentCode = '" +
// Here -------------------^
ds.Tables[0].Rows[x].ItemArray.GetValue(0).ToString() +
"'" // And here
, myConnection);
Mandatory comment: concatinating strings in order to create SQL statements may leave your code exposed to SQL injection attacks. You should consider using prepared statements instead.
I try to prevent SQL injection in SQL query. I used following code to do it but unfortunately I faced some problem. The query is not running in oracle DB:
strQuery = #"SELECT PASSWORD FROM IBK_USERS where upper(user_id) =upper(:UserPrefix) AND user_suffix=:UserSufix AND STATUS_CODE='1'";
//strQuery = #"SELECT PASSWORD FROM IBK_CO_USERS where user_id = '" + UserPrefix + "' AND user_suffix='" + UserSufix + "' AND STATUS_CODE='1'";
try
{
ocommand = new OracleCommand();
if (db.GetConnection().State == ConnectionState.Open)
{
ocommand.CommandText = strQuery;
ocommand.Connection = db.GetConnection();
ocommand.Parameters.Add(":UserSufix", OracleDbType.Varchar2,ParameterDirection.Input);
ocommand.Parameters[":UserSufix"].Value = UserSufix;
ocommand.Parameters.Add(":UserPrefix", OracleDbType.Varchar2,ParameterDirection.Input);
ocommand.Parameters[":UserPrefix"].Value = UserPrefix.ToUpper();
odatareader = ocommand.ExecuteReader();
odatareader.Read();
if (odatareader.HasRows)
{
Your parameters shouldn't contain the semicolon :. This is just an indicator in your query that the variable that follows is a parameter, but you don't have to supply that on the .NET side:
ocommand.Parameters["UserSufix"] = ...
I'm getting a syntax error in this SQL, and can't seem to figure out why?
The SQL UPDATE returns this on the error:
UPDATE Tankstationer
SET Long='12.5308724', Lat='55.6788735'
WHERE Id = 2;
Here's my code:
foreach (var row in reader)
{
var id = reader.GetInt32(0);
var adress = reader.GetString(1);
var zip = reader.GetDouble(2);
var city = reader.GetString(3);
var adressToParse = adress + " " + zip + " " + city;
GMapGeocoder.Containers.Results result = Util.Geocode(adressToParse, key);
foreach (GMapGeocoder.Containers.USAddress USAdress in result.Addresses )
{
var google_long = convertNumberToDottedGoogleMapsValid(USAdress.Coordinates.Longitude);
var google_lat = convertNumberToDottedGoogleMapsValid(USAdress.Coordinates.Latitude);
Message.Text = "Lattitude: " + google_long + System.Environment.NewLine;
Message.Text = "Longitude: " + google_lat + System.Environment.NewLine;
string updatesql = "UPDATE Tankstationer SET Long='" +google_long+ "', Lat='" +google_lat+ "' WHERE Id = " +id+"";
OleDbCommand update = new OleDbCommand();
update.CommandText = updatesql;
update.Connection = conn;
reader = update.ExecuteReader();
Message.Text = "Done";
}
}
The error is probably because you are executing a reader, but your query does not return anything. Call update.ExecuteNonQuery() instead.
"Long" is a reserved word in Access. If you can't change the schema to call that column something else, put it in brackets:
UPDATE Tankstationer
SET [Long]='12.5308724', Lat='55.6788735'
WHERE Id = 2;
try using update.ExecuteNonQuery() instead of reader.
Saw other comments too late.
I don't use access often, but mine it's using <"> for text delimiter, not <'>
Try:
"id" is being set to Int32 (var id = reader.GetInt32(0);) but you are concatenating it to a string (WHERE Id = " +id+"";). Make sure that id is cast as a string value and not an int.
I have a question about Entity Framework. Please answer if you know answer on this. I have such query :
String queryRaw =
"SELECT " +
"p.ProductName AS ProductName " +
"FROM ProductEntities.Products AS p " +
"INNER JOIN CategoryEntities.Categories AS c " +
"ON p.CategoryID = c.CategoryID ";
ObjectQuery<DbDataRecord> query = new ObjectQuery<DbDataRecord>(queryRaw, entityContext);
GridView1.DataSource = query;
GridView1.DataBind();
Particularly I want to join few tables in one query, but I can NOT use LINQ and can NOT use ObjectQuery with objects mapped to DB fields inside my query. Because each entity creates dynamically. So this is what i can NOT use :
msdn.microsoft.com/en-us/library/bb425822.aspx#linqtosql_topic12
msdn.microsoft.com/en-us/library/bb896339%28v=VS.90%29.aspx
The question is can I use something like this instead of using objects?
query.Join ("INNER JOIN CategoryEntities.Category ON p.CategoryID = c.CategoryID ");
The purpose is to use Join method of ObjectQuery with syntax as in Where method :
msdn.microsoft.com/en-us/library/bb338811%28v=VS.90%29.aspx
Thanks, Artem
Any decision i see right now is to temporary convert ObjectQuery to string, add joined table as string and then convert it back to ObjectQuery :
RoutesEntities routesModel = new RoutesEntities(entityConnection);
String queryRaw = "SELECT " +
"rs.RouteID AS RouteID, " +
"rs.LocaleID AS LocaleID, " +
"rs.IsSystem AS IsSystem " +
"FROM RoutesEntities.Routes AS rs ";
_queryData = new ObjectQuery<DbDataRecord>(queryRaw, routesModel);
var queryJoin = _queryData.CommandText + " INNER JOIN LocalesEntities.Locales AS ls ON ls.LocaleID = rs.LocaleID ";
_queryData = new ObjectQuery<DbDataRecord>(queryJoin, routesModel);
Maybe someone has more consistent suggestions?
Finally I Found a better solution for this, we can use Sub Query inside main Query. For example :
var db = CustomEntity();
ObjectQuery<Categories> query1 = db.Categories.Where("it.CategoryName='Demo'").Select ("it.CategoryID");
var categorySQL = query1.ToTraceString().Replace("dbo", "CustomEntity"); // E-SQL need this syntax
ObjectQuery<Products> query2 = db.Categories.Where("it.CategoryID = (" + categorySQL + ")");
Some example is here :
http://msdn.microsoft.com/en-us/library/bb896238.aspx
Good luck!
I have an sql statement that is supposed to return 2 rows. the first with psychological_id = 1, and the second, psychological_id = 2. here is the sql statement
select * from psychological where patient_id = 12 and symptom = 'delire';
But with this code, with which I populate an array list with what is supposed to be 2 different rows, two rows exist, but with the same values: the second row.
OneSymptomClass oneSymp = new OneSymptomClass();
ArrayList oneSympAll = new ArrayList();
string connStrArrayList = "Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\PatientMonitoringDatabase.mdf; " +
"Initial Catalog=PatientMonitoringDatabase; " +
"Integrated Security=True";
string queryStrArrayList = "select * from psychological where patient_id = " + patientID.patient_id + " and symptom = '" + SymptomComboBoxes[tag].SelectedItem + "';";
using (var conn = new SqlConnection(connStrArrayList))
using (var cmd = new SqlCommand(queryStrArrayList, conn))
{
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
oneSymp.psychological_id = Convert.ToInt32(rdr["psychological_id"]);
oneSymp.patient_history_date_psy = (DateTime)rdr["patient_history_date_psy"];
oneSymp.strength = Convert.ToInt32(rdr["strength"]);
oneSymp.psy_start_date = (DateTime)rdr["psy_start_date"];
oneSymp.psy_end_date = (DateTime)rdr["psy_end_date"];
oneSympAll.Add(oneSymp);
}
}
conn.Close();
}
OneSymptomClass testSymp = oneSympAll[0] as OneSymptomClass;
MessageBox.Show(testSymp.psychological_id.ToString());
the message box outputs "2", while it's supposed to output "1". anyone got an idea what's going on?
You're adding the same instance to the ArrayList twice. Try this:
List<OneSymptomClass> oneSympAll = new List<OneSymptomClass>();
string connStrArrayList =
"Data Source=.\\SQLEXPRESS;" +
"AttachDbFilename=|DataDirectory|\\PatientMonitoringDatabase.mdf; " +
"Initial Catalog=PatientMonitoringDatabase; " +
"Integrated Security=True";
Patient patientID;
string queryStrArrayList =
"select * from psychological where patient_id = " +
patientID.patient_id + " and symptom = '" +
SymptomComboBoxes[tag].SelectedItem + "';";
using (var conn = new SqlConnection(connStrArrayList))
{
using (var cmd = new SqlCommand(queryStrArrayList, conn))
{
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
OneSymptomClass oneSymp = new OneSymptomClass();
oneSymp.psychological_id =
Convert.ToInt32(rdr["psychological_id"]);
oneSymp.patient_history_date_psy =
(DateTime) rdr["patient_history_date_psy"];
oneSymp.strength = Convert.ToInt32(rdr["strength"]);
oneSymp.psy_start_date =
(DateTime) rdr["psy_start_date"];
oneSymp.psy_end_date =
(DateTime) rdr["psy_end_date"];
oneSympAll.Add(oneSymp);
}
}
conn.Close();
}
}
MessageBox.Show(oneSympAll[0].psychological_id.ToString());
MessageBox.Show(oneSympAll[1].psychological_id.ToString());
Note that I replaced the ArrayList with a List<OneSymptomClass>. There is no reason to use ArrayList unless you're using .NET 1.1.
thx for the tip John Saunders. I added a line that makes it work. was that what you were gonna suggest me?
while (rdr.Read())
{
oneSymp = new OneSymptomClass();
oneSymp.psychological_id = Convert.ToInt32(rdr["psychological_id"]);
oneSymp.patient_history_date_psy = (DateTime)rdr["patient_history_date_psy"];
oneSymp.strength = Convert.ToInt32(rdr["strength"]);
oneSymp.psy_start_date = (DateTime)rdr["psy_start_date"];
oneSymp.psy_end_date = (DateTime)rdr["psy_end_date"];
oneSympAll.Add(oneSymp);
}