Error in SQL query when searching - c#

I have an SQL query that displays information from different tables in the database. This query is then displayed in a DataGrid and I have some options in a DropDownList to search through the DataGrid for certain values. The problem is the search doesn't display the correct information for CollectName or DeliverName.
Code for DropDownList:
private static readonly Dictionary<string, string> SearchFields = new Dictionary<string, string> {
{ "Customer", "c.Name" },
{ "Department", "jn.Department" },
{ "CollectName", "SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID" },
{ "DeliverName", "(SELECT Name FROM job_address WHERE AddressType = 2 AND JobID = jn.ID)" }
};
In the SQL query CollectName and DeliverName are inner select statements and that's whats causing the problem here because the search for Customer and Department work fine.
The SQL query:
SELECT c.Name,
COUNT(distinct jn.ID) as Jobs,
sum(jn.OutTurn) as Outturn,
SUM(jn.ActualWeight) as GrossWt,
SUM(jn.CBM) as CBM,
jn.Department,
(SELECT Name FROM job_address WHERE AddressType =3 AND JobID = jn.ID) as CollectName,
(SELECT Name FROM job_address WHERE AddressType =2 AND JobID = jn.ID) as DeliverName
FROM customer c
LEFT JOIN job_address ja ON c.AccountCode = ja.Code AND c.Company_ID = ja.Company_ID
JOIN AddressType jat ON ja.AddressType = jat.ID and jat.Description = 'Debtor'
LEFT JOIN job_new jn ON ja.JobID = jn.ID
WHERE c.Company_ID = ?compid
GROUP BY c.ID
I have a search function that takes the value selected from the DropDownList and the value entered in the textbox:
List<MySqlParameter> param = new List<MySqlParameter>{ new MySqlParameter("compid", CompanyID) };
StringBuilder SQL = new StringBuilder(SearchSQL);
if (SearchFieldKey != null && SearchFieldKey.Length > 0)
{
SQL.Append(" AND (");
for (int i = 0; i < SearchFieldKey.Length; i++)
{
if (SearchFields.ContainsKey(SearchFieldKey[i]))
{
SQL.Append(SearchFields[SearchFieldKey[i]] + " LIKE ?parameter" + i.ToString());
param.Add(new MySqlParameter("parameter" + i.ToString(), "%" + SearchTerms[i] + "%"));
if (i != SearchFieldKey.Length - 1)
SQL.Append(" OR ");
}
else
throw new Exception("Error: Attempted to search on invalid field. Check SearchFields Argument.");
}
SQL.Append(") ");
}
So for example I search for a customer, the SQL query get this line added to end:
WHERE c.Company_ID = ?compid AND (c.Name LIKE ?parameter0)
And when I search for CollectName or DeliverName the query is this:
WHERE c.Company_ID = ?compid AND (SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID LIKE ?parameter0)
Is there a problem with this SQL query that causes CollectName and DeliverName not to work?

The parenthesis doesn't match, it should be
WHERE c.Company_ID = ?compid
AND (SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID) LIKE ?parameter0
To solve this, you can in your dictionary embed the statement:
{ "CollectName", "(SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID)" },
Or in your method that build the SQL, embed automatically the subquery:
SQL.Append("(" + SearchFields[SearchFieldKey[i]] + ") LIKE ?parameter" + i.ToString());
Full correction : you should not try to concatenate string together if you are using a StringBuilder:
var param = new List<MySqlParameter> { new MySqlParameter("compid", CompanyID) };
StringBuilder SQL = new StringBuilder(SearchSQL);
if (SearchFieldKey != null && SearchFieldKey.Length > 0)
{
SQL.Append(" AND (");
for (int i = 0; i < SearchFieldKey.Length; i++)
{
if (SearchFields.ContainsKey(SearchFieldKey[i]))
{
SQL.Append("(");
SQL.Append(SearchFields[SearchFieldKey[i]]);
SQL.Append(") LIKE ?parameter");
SQL.Append(i);
param.Add(new MySqlParameter("parameter" + i.ToString(), "%" + SearchTerms[i] + "%"));
if (i != SearchFieldKey.Length - 1)
SQL.Append(" OR ");
}
else
throw new Exception("Error: Attempted to search on invalid field. Check SearchFields Argument.");
}
SQL.Append(") ");
}

Related

SQL timeout occurs while getting the count after filter/search the dynamically modifed value in the IQueryable

In our project we have used the below code to populate the data to a table. While getting the count after filtering or searching the below highlighted "Exceeded" field we are getting the timeout from SQL .
Screenshot attached
IQueryable data = _context.Orders;
IQueryable<SelectList> dataSource = (from t in _context.Orders
select new
{
t.OrderID,
t.CustomerID,
t.EmployeeID,
t.ShipCity,
CustomerName = t.Employees.FirstName,
EmployeeName = t.Employees.FirstName + " " + t.ShipCity,
cashier = _context.Orders.Where(c => c.OrderID == t.OrderID).Select(c => c.CustomerID + " " + c.ShipCity)
}).Select(t => new SelectList()
{
CustomerName = t.CustomerName,
ShipCity = t.ShipCity,
EmployeeName = t.EmployeeName,
**Exceeded = t.EmployeeID > 5 ? "Yes" : "No"**
}).AsQueryable();

Dynamic filters and columns using command.parameters()

I'm tring to create a dynamic query based on user input (textbox) so the user fills any textbox and the command adapts both the columns and WHERE conditionals.
So what i've done is create arrays for both parameters and the for statement scans each non-null values in the conditionals array, based on it I create sql .Parameters, one fills columns that will be displayed and the other applies conditionals.
The problem it's obviusly that the for adds the wrong sintaxis for the sql SELECT, so instead of this:
SELECT [COL_N],[COL_N+1] FROM [TABLE1] WHERE [CONDITION_N] LIKE '%[VALUE]%' AND [CONDITION_N+1] = '%[VALUE]%'
I get this:
SELECT [COL_N],[COL_N+1], FROM [TABLE1] WHERE [CONDITION_N] LIKE '%[VALUE]%' AND [CONDITION_N+1] = '%[VALUE]%' AND
This is my code
var arregloParametro = new[] { col1, col2, col3, col4, col5, col6, col7 };
var arregloColumnas = new[] { val1, val2, val3, val4, val5, val6, val7 };
try
{
connSQL.Open();
// PROCESO BUSCAR
SqlCommand selProc = connSQL.CreateCommand();
SqlDataAdapter adaptadorDatos1 = new SqlDataAdapter(selProc);
for (int i = 0; i < arregloParametro.Length; i++)
{
if (arregloParametro[i] != null || arregloParametro[i] != "")
{
string colQuery = arregloColumnas[i] + ", ";
string filPar = arregloColumnas[i] + " LIKE '" + arregloParametro[i] + "' AND ";
selProc.Parameters.AddWithValue("#cmdCol", arregloColumnas[i] + ", ");
selProc.Parameters.AddWithValue("#cmdFil", arregloColumnas[i] + " LIKE '%" + arregloParametro[i] + "%' AND ");
lblColFil.Text = colQuery;
lblColUsadas.Text = filPar;
}
}
selProc.CommandText = "SELECT #cmdCol FROM tTest WHERE #cmdFil;";
selProc.CommandType = CommandType.Text;
DataTable tablaProcRes = new DataTable();
adaptadorDatos1.Fill(tablaProcRes);
gridBuscarRUT.DataSource = tablaProcRes;
gridBuscarRUT.DataBind();
gridBuscarRUT.Visible = true;
connSQL.Close();
}
catch (Exception ex)
{
lblConexion.Visible = true;
lblConexion.Text = ex.Message.ToString();
}
I'm looking for a better approach rather than fixing this block of code. Thanks!
using string.join (and modifying your datastructure) i can generate the colquery and parameters in 2 lines
var input = new[]{new Tuple<string,string>(col1,val1),new Tuple<string,string>(col2,val3),...};
....
string colQuery = string.Join(", ", input.Where(i => i.Item1 != null || i.Tiem2 != null));
string filPar = string.Join(" AND ", input.Where(i => i.Item1 != null || i.Tiem2 != null).Select(i => $"{i.item1} LIKE {i.item2}" ))
hope this helps

How do I build a dynamic Where clause with Dapper when passing in a model

I have an example model that looks like this:
public class PersonModel
{
public int Id {get; set;}
public string FirstName {get; set;}
public string Lastname {get; set;}
public string City {get; set;}
}
In my repository I want to create a search method where I pass in my model - but not all fields will always be populated. I want to create a WHERE and AND based on if a field in the model is populated or not. If the field is not populated then I do not want to create a WHERE clause for it.
For example - if I pass in FirstName = "Bob" and City = "Boston" then I want my search to look like this:
SELECT * FROM PersonTable WHERE FirstName = #firstName AND City = #city
Since I did not pass in Id or LastName I don't want them added to the query. If I just pass in City = "Boston" then I want it to look like this:
SELECT * FROM PersonTable WHERE City = #city
My repo method would look something like this
using Dapper;
public List<PersonModel> Search(PersonModel model)
{
//db = DbConnection connection
var selectSql = "SELECT * FROM PersonTable "; //build out where clause somehow
return db.Query<PersonModel>(selectSql).ToList();
}
My question is how would I build this out in my repo method properly?
You can also use Dapper's SqlBuilder.
Note that you'll have to install the Dapper.SqlBuilder NuGet package since it doesn't come with Dapper's main distribution.
Here is an example:
[Test]
public void Test()
{
var model = new PersonModel {FirstName = "Bar", City = "New York"};
var builder = new SqlBuilder();
//note the 'where' in-line comment is required, it is a replacement token
var selector = builder.AddTemplate("select * from table /**where**/");
if (model.Id > 0)
builder.Where("Id = #Id", new { model.Id });
if (!string.IsNullOrEmpty(model.FirstName))
builder.Where("FirstName = #FirstName", new { model.FirstName });
if (!string.IsNullOrEmpty(model.Lastname))
builder.Where("Lastname = #Lastname", new { model.Lastname });
if (!string.IsNullOrEmpty(model.City))
builder.Where("City = #City", new { model.City });
Assert.That(selector.RawSql, Is.EqualTo("select * from table WHERE FirstName = #FirstName AND City = #City\n"));
//var rows = sqlConnection.Query(selector.RawSql, selector.Parameters);
}
You can find some examples here.
This should do the trick for you, clean and simple:
var selectSql = "SELECT * FROM PersonTable WHERE (#FirstName IS NULL OR FirstName = #FirstName) AND (#LastName IS NULL OR LastName = #LastName) AND (#City IS NULL OR City = #City) AND (#Id IS NULL OR Id = #Id) OPTION(RECOMPILE)";
return conn.Query<PersonModel>(selectSql, new
{
model.FirstName,
model.Lastname,
model.City,
Id = model.Id == 0? (int?)null: (int?)model.Id
}).ToList();
DapperQueryBuilder is an alternative to Dapper.SqlBuilder but much easier to use:
var query = cn.QueryBuilder($"SELECT * FROM PersonTable WHERE 1=1");
if (model.Id > 0)
query += $"AND Id = {model.Id}";
if (!string.IsNullOrEmpty(model.FirstName))
query += $"AND FirstName = {model.FirstName}";
if (!string.IsNullOrEmpty(model.Lastname))
query += $"AND Lastname = {model.Lastname}";
if (!string.IsNullOrEmpty(model.City))
query += $"AND City = {model.City}";
var results = query.Query<Person>();
Query<Person>() will invoke Dapper passing the underlying SQL and parameters - and the underlying query is fully parametrized SQL (WHERE FirstName = #p0 AND LastName = #p1, etc). Parameters are automatically captured from the string interpolation (but it's safe against SQL injection).
--
ALTERNATIVE SYNTAX
Similar to Dapper.SqlBuilder you can also use the /**where**/ syntax (which will automatically join the conditions, no need to manually use AND or 1==1 trick):
var query = cn.QueryBuilder($#"
SELECT *
FROM PersonTable
/**where**/
");
if (model.Id > 0)
query.Where($"Id = {model.Id}");
if (!string.IsNullOrEmpty(model.FirstName))
query.Where($"FirstName = {model.FirstName}");
if (!string.IsNullOrEmpty(model.Lastname))
query.Where($"Lastname = {model.Lastname}");
if (!string.IsNullOrEmpty(model.City))
query.Where($"City = {model.City}");
var results = query.Query<Person>();
Disclaimer: I'm the author of the library
bool isFirstWhereSet = false;
bool isCityWhereSet = false;
string sqlQuery = "SELECT * FROM PersonTable " ;
if (! String.IsNullOrEmpty(model.FirstName ))
{
sqlQuery =sqlQuery + "WHERE FirstName =#FirstName" ;
isFirstWhereSet = true;
}
if (! String.IsNullOrEmpty(model.City))
{
isCityWhereSet = true ;
if (! isFirstWhereSet )
sqlQuery = sqlQuery + " WHERE City = #city";
else
sqlQuery = sqlQuery + " AND City = #city";
}
if (isFirstWhereSet == true && isCityWhereSet == true )
return db.Query<PersonModel>(sqlQuery , new { FirstName = model.FirstName , City = mode.City}).ToList();
else if (isFirstWhereSet == true && isCityWhereSet == false)
return db.Query<PersonModel>(sqlQuery , new { FirstName = model.FirstName }).ToList();
else if (isFirstWhereSet == false && isCityWhereSet == true)
return db.Query<PersonModel>(sqlQuery , new { City= model.City}).ToList();
else
{
return db.Query<PersonModel>(sqlQuery).ToList();
}
You can use the ExpressionExtensionSQL library. This library converts lambda expressions to where clauses, and can be used with dapper and ADO.

c# how to build a dynamic query in the where condition

This is my query
SELECT COUNT(MPRO.OriDes0154) 'QueueCalls' FROM MMProdat.dbo.InstTaWf0154 MPRO WITH(NOLOCK)
WHERE MPRO.Estado0154 = 'QUEUED'
AND F1IdWI02140154<>'VOICEMAIL'
AND F1Camp02930154 = 'Support'
please notice that i have AND F1Camp02930154 = 'Support'
Now I have a list like this:
List<string> compaines = new List<string>();
the values in this list should be in this conidtion AND F1Camp02930154 = 'Support'
for example if the list is empty, i will get an empty result, but if the list has just one value which is Support the query will be
AND F1Camp02930154 = 'Support'
but if the list has two vaules which are Support and Sales then the query will be
AND F1Camp02930154 = 'Support' and `Sales`
how to do that please in c#
where I already have this:
string queryString = "SELECT COUNT(MPRO.OriDes0154) 'QueueCalls' FROM MMProdat.dbo.InstTaWf0154 MPRO WITH(NOLOCK) WHERE MPRO.Estado0154 = 'QUEUED' AND F1IdWI02140154<>'VOICEMAIL'";
Update 1
After # Gordon Linoff comment
I tried this:
List<string> compaines = new List<string>();
string queryString = "SELECT COUNT(MPRO.OriDes0154) 'QueueCalls' FROM MMProdat.dbo.InstTaWf0154 MPRO WITH(NOLOCK) WHERE MPRO.Estado0154 = 'QUEUED' AND F1IdWI02140154<>'VOICEMAIL' AND F1Camp02930154 IN (";
for (int i = 0; i < compaines.Count(); i++) {
queryString += "'" + compaines[i] + "'";
}
queryString += ")";
You can use String.Join() to construct IN () clause, for example :
var companies = new List<string>() { "Support", "Sales" };
string inClause = String.Format(" IN ('{0}')", String.Join("', '", companies));
Console.WriteLine("AND F1Camp02930154" + inClause);
//output :
//AND F1Camp02930154 IN ('Support', 'Sales')

There was an error parsing the query

I am writing a program in C# using Visual Studio 2010 and gets an error when retrieving data from a .sdf file.
private void BasicSearch_button_Click(object sender, EventArgs e)
{
SqlCeConnection cn = new SqlCeConnection(#"Data Source=" + Path.Combine(Application.StartupPath, "FRAT_DB.sdf") + ";Password=P#ssw0rd;Persist Security Info=False;Max Database Size=256");
string query = #"SELECT Person.PersonID, Person.Name, PhotoTags.PhotoID, Photo.Path, Photo.Location, Person.Age, Person.P_Group, Person.Email, Photo.Date "+
"FROM Person INNER JOIN PhotoTags ON Person.PersonID = PhotoTags.PersonID INNER JOIN "+
"PhotoON PhotoTags.PhotoID = Photo.PhotoID "+"WHERE (Person.Name LIKE '%%')";
if (txtName1.Text.Trim().ToString() == "" || txtName2.Text.Trim().ToString() == "")
{ MessageBox.Show("Enter Both, Name1 and Name2"); return; }
else if (radioButtonAND.Checked)
{
query = #"select pt.PhotoID,Photo.Path,photo.Location, Photo.Date from Person p inner join PhotoTags pt on p.PersonID=pt.PersonID inner join Photo on pt.photoID=photo.photoID where Name like '%" + txtName1.Text + "%' and pt.Photoid in (select pt.PhotoID from Person p inner join PhotoTags pt on p.PersonID=pt.PersonID where Name like '%" + txtName2.Text + "%')";
}
else
{
query = #"SELECT DISTINCT Person.PersonID, Person.Name, PhotoTags.PhotoID, Photo.Path, Photo.Location, Person.Age, Photo.Date
FROM Person INNER JOIN PhotoTags ON Person.PersonID = PhotoTags.PersonID INNER JOIN
PhotoON PhotoTags.PhotoID = Photo.PhotoID
WHERE (Person.Name LIKE '%%')";
query += " AND (Person.Name like '%" + txtName1.Text + "%' OR Person.Name like '%" + txtName2.Text + "%')";
}
if (cn.State == ConnectionState.Closed) cn.Open();
SqlCeCommand cm_Search = new SqlCeCommand(query, cn);
try
{
SqlCeDataReader rdr = cm_Search.ExecuteReader();
List<PersonPhoto> personPhoto = new List<PersonPhoto>();
List<PersonPhotoWithoutName> personPhotoWithoutName = new List<PersonPhotoWithoutName>();
bool HasRows = rdr.Read();
if (HasRows)
{
while (rdr.Read())
{
if (radioButtonAND.Checked)
{
personPhotoWithoutName.Add(new PersonPhotoWithoutName
{
PhotoID = Convert.ToInt32(rdr["PhotoID"].ToString()),
Location = rdr["location"].ToString(),
Date = rdr["Date"] != null ? Convert.ToDateTime(rdr["Date"]) : DateTime.Now,
path = rdr["path"].ToString(),
});
}
else
{
personPhoto.Add(new PersonPhoto
{
Name = rdr["Name"].ToString(),
Location = rdr["location"].ToString(),
Date = rdr["Date"] != null ? Convert.ToDateTime(rdr["Date"]) : DateTime.Now,
path = rdr["path"].ToString()
});
}
}
rdr.Close();
}
else
{ MessageBox.Show("No Records Found"); selectedPictureBox.Image = null; rdr.Close(); }
if (personPhoto.Count > personPhotoWithoutName.Count)
{
DataGridPersons.DataSource = personPhoto;
DataGridPersons.Refresh();
}
else
{
DataGridPersons.DataSource = personPhotoWithoutName;
DataGridPersons.Refresh();
}
}
catch (Exception exp)
{
throw exp;
}
if (cn.State != ConnectionState.Closed) cn.Close();
}
Whenever I try to search for someone this exception error comes up:
There was an error parsing the query. [ Token line number = 3,Token
line offset = 18,Token in error = . ]
Any help?
thanks in advance!
I suspect this is the problem:
... INNER JOIN
PhotoON PhotoTags.PhotoID = Photo.PhotoID ...
I suspect you meant:
... INNER JOIN
Photo ON PhotoTags.PhotoID = Photo.PhotoID ...
Note the space between Photo and ON. I suspect you could reformat your query so this would be rather clearer, to be honest...

Categories

Resources