Dapper Order By - sql-order-by

Is there any reason why the following code wouldn't be retrieved in the correct order when using dapper?
connection.Query<User>("SELECT id, name " +
"FROM user " +
"ORDER BY #sort #dir " +
"LIMIT #offset, #pageSize; ",
new {
sort = sortOrder, // sortOrder = "name"
dir = sortDirection, // sortDirection = "ASC"
offset = pageIndex * pageSize, // offset = 0
pageSize = pageSize // pageSize = 10
});
It always returns without applying the ordering.
I could just place the sortOrder and sortDirection directly into the string like this
"SELECT id, name " +
"FROM user " +
"ORDER BY " + sortOrder + " " + sortDirection + " " +
"LIMIT #offset, #pageSize; "
but I'm not sure how that will effect dapper since I believe it has it's own query plan caching.
Also, is there a way to view the query that is generated by dapper?

Sure, in general database engines do not allow you to parametrize column names. So for example:
var row = cnn.Query("select #bob as col from table", new {bob = "col"}).first();
// most likely returns "row.col == col"
When you are trying to parametrize order by clauses I would recommend using inline substitution, provided you can guarantee your escaping, bobby tables is always lurking. (Either that or you could use a proc)
I am not sure about the situation around profilers for (MySQL? Oracle?) but you could use a tool like MiniProfiler to see the SQL.
It will affect caching, however there are only a small number of permutations of sortOrder and sortDirection so the impact is minimal.

You can just use a case statement in your order by. As long as you're only dealing with a few columns, it won't get too crazy.
public List<Signup> GetNext(int Id, int RowsToFetch, string SortedBy)
{
string _sortedBy = SortedBy.Trim();
using (IDbConnection conn = Connection)
{
string sQuery = #"SELECT TOP(#ROWSTOFETCH)
[Id]
,[FirstName]
,[LastName]
,[EmailAddress]
FROM [dbo].[vwBaseQuery]
WHERE ID >= #ID
ORDER BY
CASE WHEN #SORTEDBY = 'Id ASC' THEN Id END ASC,
CASE WHEN #SORTEDBY = 'Id DESC' THEN Id END DESC,
CASE WHEN #SORTEDBY = 'FirstName ASC' THEN FirstName END ASC,
CASE WHEN #SORTEDBY = 'FirstName DESC' THEN FirstName END DESC,
CASE WHEN #SORTEDBY = 'LastName ASC' THEN LastName END ASC,
CASE WHEN #SORTEDBY = 'LastName DESC' THEN LastName END DESC,
CASE WHEN #SORTEDBY = 'EmailAddress ASC' THEN EmailAddress END ASC,
CASE WHEN #SORTEDBY = 'EmailAddress DESC' THEN EmailAddress END DESC";
conn.Open();
var result = conn.Query<Signup>(sQuery, new {
SORTEDBY = _sortedBy,
ROWSTOFETCH = RowsToFetch,
ID = Id
}).ToList();
return result;
}
}

Related

Link OrderBy method not taking effect after Union method

I'm using LINQ's Union method to combine two or more collections. After that I'm trying to apply sorting to the combined collection by calling OrderBy on a field that is common to the collections. Here is how I am applying sorting:
combinedCollection.OrderBy(row => row["common_field"]);
combinedCollection is defined as:
Enumerable<DataRow> combinedCollection;
I need the sorting to be applied to the entire combined collection. For some reason, that is not happening. Instead I see there is sorting applied on some other field separately within each 'collection' block within the combined collection
And idea why??
First Edit
foreach (....)
{
if (combinedCollection != null)
{
combinedCollection = combinedCollection.Union(aCollection);
}
else
{
combinedCollection = aCollection;
}
}
Second Edit
_Cmd.CommandText = "SELECT Person.Contact.FirstName, Person.Contact.LastName, Person.Address.City, DATEDIFF(YY, HumanResources.Employee.BirthDate, GETDATE()) AS Age"
+ " FROM HumanResources.EmployeeAddress INNER JOIN"
+ " HumanResources.Employee ON HumanResources.EmployeeAddress.EmployeeID = HumanResources.Employee.EmployeeID INNER JOIN"
+ " Person.Address ON HumanResources.EmployeeAddress.AddressID = Person.Address.AddressID INNER JOIN"
+ " Person.Contact ON HumanResources.Employee.ContactID = Person.Contact.ContactID AND HumanResources.Employee.ContactID = Person.Contact.ContactID AND "
+ " HumanResources.Employee.ContactID = Person.Contact.ContactID AND HumanResources.Employee.ContactID = Person.Contact.ContactID";
DataTable employeeTable = new DataTable();
_Adpt.Fill(employeeTable);
DataRow[] allRows = null;
allRows = employeeTable.Select("");
IEnumerable<DataRow> filteredEmployeeRows;
filteredEmployeeRows = from row in allRows select row;
// Declare a variable to hold the city-filtered rows and set it to null for now
IEnumerable<DataRow> cityFilteredEmployeeRows = null;
//Copy filtered rows into a data table
DataTable filteredEmployeeTable = filteredEmployeeRows.CopyToDataTable<DataRow>();
foreach (DataRowView city in CityListBox.SelectedItems)
{
// create an exact copy of the data table
DataTable filteredEmployeeCopyTable = filteredEmployeeTable.Copy();
// Enumerate it
IEnumerable<DataRow> filteredEmployeeRowsInSingleCity = filteredEmployeeCopyTable.AsEnumerable();
// Apply the city filter
filteredEmployeeRowsInSingleCity = _ApplyCityFilter(filteredEmployeeRowsInSingleCity, city["City"].ToString());
if (cityFilteredEmployeeRows != null)
{
// Combine the filtered rows for this city with the overall collection of rows
cityFilteredEmployeeRows = cityFilteredEmployeeRows.Union(filteredEmployeeRowsInSingleCity);
}
else
{
cityFilteredEmployeeRows = filteredEmployeeRowsInSingleCity;
}
}
//apply ordering
cityFilteredEmployeeRows.OrderBy(row => row["Age"]);
//cityFilteredEmployeeRows.OrderByDescending(row => row["Age"]);
EmployeeGridView.DataSource = cityFilteredEmployeeRows.CopyToDataTable<DataRow>();
.......
private IEnumerable<DataRow> _ApplyCityFilter(IEnumerable<DataRow> filteredEmployeeRows, string city)
{
IEnumerable<DataRow> temp = filteredEmployeeRows;
filteredEmployeeRows = from row in temp
where row["City"].ToString() == city
select row;
return filteredEmployeeRows;
}
I think you have a problem with the LINQ lazy evaluation, I would have to investigate to find out wich part causes the problem.
Using the foreach(var item...) in lazy functions has already bitten me (because when executed later they all reference the last iterated item), but in your case it doesn't look like this is the problem.
To check it is the really the issue you can just use a DataRow[] in place of the IEnumerable<DataRow> and call .ToArray() after every LINQ function.
Edit: I'm not sure I got your code right but can't you just use:
var cities = CityListBox.SelectedItems.Cast<DataRowView>()
.Select(city => city["City"].ToString())
.ToArray();
var rows = allRows
.Where(r => cities.Contains((string)r["City"]))
.OrderBy(r => (int?)r["Age"])
.ToArray(); // if you want to evaluate directly, not mandatory

display results on query basis in Codeigniter with Pagination

I have a table of products. I need to display all the products. I am fetching results on different conditions.
create table `products` (
`id` double ,
`category_id` double ,
`subcategory_id` double ,
`product_name` varchar (765),
`product_description` varchar (765),
`product_viewed` varchar (765),
`sale_wanted` tinyint (2),
`added_date` datetime ,
`updated_date` datetime ,
);
I need to diplay the results like this
1. The latest products (use of added date)
2. Most Wanted (Sorting by sale_wanted 1 for sale , 2 for wanted)
3. Most Viewed (Sorting by product_viewed)
4. Sorting by Specific Subcategory
All the results should display with pagination. This is all right if i first get the result. But if i walk with pagination links all the condition data is lost and the query fetches the results without any condition. How can i manage This situation. Please i dont need Code i need hints and suggestions. The other thing is that i am using Codeigniter's pagination class.
EDITED
Here is my Model Method i am using
public function getProductsList($per_page=5,$page=0)
{
$info = $this->input->post();
if(isset($info['type']))
{
$type = $info['type'];
if($type == 'most_wanted'){
$where = " AND sale_wanted = 1";
$order_by = " ORDER BY ldc.added_date desc";
}else if($type == 'most_viewed'){
$where = " ";
$order_by = " ORDER BY ldc.product_viewed desc";
}else{
$where = " ";
$order_by = " ORDER BY ldc.added_date desc";
}
}else if(isset($info['sale_wanted']) AND isset($info['subcategory_id'])){
$sale_wanted = $info['sale_wanted'];
$subcategory_id = $info['subcategory_id'];
$where = " AND sale_wanted = $sale_wanted AND ldc.subcategory_id = $subcategory_id";
$order_by = " ORDER BY ldc.added_date desc";
}else if(isset($info['keyword'])){
$keyword = $info['keyword'];
$search_type = $info['search_type'];
$where = " AND ldc.$search_type like '$keyword%'";
$order_by = " ";
}else{
$where = " ";
$order_by = " ";
}
$num = 0;
if($page != 0){
$num = ($page * $per_page) - $per_page;
}
$sql_query = "
SELECT
ldc.id,
ldc.product_name,
ldc.product_viewed,
DATE_FORMAT(ldc.added_date, '%m/%d/%Y') as added_date,
ifnull(dc.name,'Unknown') as category,
dpi.product_image
FROM default_products AS ldc
LEFT JOIN default_manufacturers as dm ON dm.id = ldc.manufacturer
LEFT JOIN default_category as dc ON dc.category_id = ldc.category_id
LEFT JOIN ((select product_id , product_image from default_product_images group by product_id) as dpi)
ON dpi.product_id = ldc.id
WHERE approved = 1
$where
$order_by
LIMIT $num,$per_page
";
$query = $this->db->query($sql_query);
return $query->result();
}
i would highly recommend these two Free tutorials on codeigniter pagination -
video tutorials, working sample code ( might need to update code to CI 2.1 ), and even some helpful info in the comments.
CodeIgniter from Scratch: Displaying & Sorting Tabular Data
http://net.tutsplus.com/tutorials/php/codeigniter-from-scratch-displaying-sorting-tabular-data/
CodeIgniter from Scratch: Search Results without Query Strings
http://net.tutsplus.com/tutorials/php/codeigniter-from-scratch-search-results-without-query-strings-2/

need to return the first record linq

I need to return only the first record. Although I had to put the z.FirstOrDefault(); still more than one were displayed
public DataTable GetDependents(string EmployeeID)
{
HealthCareSystem.DataClassesDataContext db = new HealthCareSystem.DataClassesDataContext();
var z = (from s in db.SelectingDependentsGroupBies
where s.EmployeeID.Equals(EmployeeID)
join d in db.Dependents on s.DependentID equals d.DependentID
orderby s.DependentID descending
//Selecting wanted tables dependents fields by datatable
select new DependentsX { DependentID = Convert.ToInt32(s.DependentID), EmployeeID = s.EmployeeID, Name = s.Name, Surname = s.Surname, IDCardNo = s.IDCardNo, ContactNo = s.ContactNo, BirthDate = s.BirthDate, StartSchemeDate = s.StartDate, EndSchemeDate = s.EndDate, RelationType = s.Type, Payment = Convert.ToDouble(d.Payment), });
var firstRecord = z.FirstOrDefault();
Add this line after your existing code:
var firstRecord = x.First();
If there might be zero results and you don't want an exception then you can use FirstOrDefault instead.
var firstRecord = x.FirstOrDefault();
Another common way is to enclose your query in parenthesis and then include the extension methods outside the parenthesis:
var x = (from d in db.DependentsRelationsViews
where d.IDCardNo.Equals(DepID)
orderby d.DependentID descending
select new DependentsX
{
DependentID = Convert.ToInt32(d.DependentID),
EmployeeID = d.EmployeeID,
Name = d.Name,
Surname = d.Surname,
IDCardNo = d.IDCardNo,
ContactNo = d.ContactNo,
BirthDate = d.BirthDate,
StartSchemeDate = Convert.ToDateTime(d.StartSchemeDate),
EndSchemeDate = d.EndSchemeDate,
Payment = d.Payment,
RelationType = Convert.ToString(d.Type)
}).FirstOrDefault();
If you don't enclose it in parenthesis and try to call FirstOrDefault() then you're applying that extension to the anonymous type in just the very last Select clause. By enclosing the whole query in parenthesis and then calling the FirstOrDefault() extension, you're indicating to the compiler that you wish to apply it to the return value of your entire query, which will be the IEnumerable<> or IQueryable<> collection.

How to speed this query up?

I'm using yii framework to create a website takes care of poems and words and stuff...
the problem is that the database has more than 250,000 record of messages and more than 500,000 records of keywords and relationships :S
what I'm trying to say that I want to make an optimal query to get all related messages of a specific message depending on key-tags..
what I did so far is caching the query, but it's still slow when opening it for the first time and that's a big problem!
here is my code:
public function getRelatedMessages()
{
$id = $this->id;
$dependency = "select `messages`.id,count(id) as tag_count from messages left join `messages_tags` on `messages`.`id` = `messages_tags`.mid
where `messages_tags`.`tagid` in (select `tags`.`id` from tags left join `messages_tags` on tags.id = `messages_tags`.tagid
where `messages_tags`.`mid` = {$id}) and id <> {$id} group by id order by tag_count desc";
$dependency = Messages::model()->cache(900)->findAllBySql($dependency);
foreach ($dependency as $dependency) {
$dependency1[] = $dependency['id'];
}
$dependency = implode(", ", $dependency1);
$sql = "select * from messages where id in ({$dependency}) limit 4";
$relateds = Messages::model()->findAllBySql($sql);
$db = array();
foreach($relateds as $related) {
$db[] = $related;
}
if(!$relateds || count($db)<4) {
$limit = 4-count($db);
$sql = "select `messages`.id, `messages`.url_key, `messages`.photo_url, `messages`.message from messages order by rand(), likes desc, comments desc, impressions desc, reported asc limit {$limit};";
$relateds = Messages::model()->cache(900)->findAllBySql($sql);
foreach($relateds as $related) {
$db[] = $related;
}
}
return $db;
}
the code above selects only 4 records from the related messages after filtering them and ordering them and stuff.. the problem is with "order by" but I need it :S
sorry if that was too long..
thank you :)
You could improve the first query by not using findAllBySql() and instantiating all the ActiveRecord classes. You don't need them, so use plain sql query like this (query stays the same but code around has changed):
// ...
$dependency = $this->dbConnection->createCommand("
select `messages`.id, count(id) as tag_count
from messages left join `messages_tags` on `messages`.`id` = `messages_tags`.mid
where
`messages_tags`.`tagid` in
(select `tags`.`id` from tags left join `messages_tags` on tags.id = `messages_tags`.tagid where `messages_tags`.`mid` = {$id})
and id <> {$id}
group by id order by tag_count desc")->queryColumn();
$dependency = implode(", ", $dependency);
$sql = "select * from messages where id in ({$dependency}) limit 4";
$relateds = Messages::model()->findAllBySql($sql);
// ...

Conditional Multiple Fields Searching and Filtering in LINQ

Assuming that we have the following table:
Person:
PersonID,
Name,
Age,
Gender
And we are providing a search function that allows users to search the table according to the name and/or the age.
The tricky part in writing the SQL ( or LINQ) query is that the users can choose to search for both field, or any one field, or no field. If he wants to search for all then he would just have to leave the textbox blank.
The logic to do this can be written as follows:
var p;
if(Name_TextBox=='')
{
p=from row in person
select row ;
}
else
{
p= from row in person
where row.Name=Name_TextBox
select row ;
}
// repeat the same for age
Now after a while the code gets very long and messy... How can I compress the above into a single query with no if-else?
Try code like this
string personName = txtPersonName.Text;
int personAge = Convert.ToInt32(txtAge.Text);
var opportunites = from p in this.DataContext.Persons
select new
{
p.PersonID,
p.Name,
p.Age,
p.Gender
};
if (personsID != 0)
opportunites = opportunites.Where(p => p.PersonID == personID);
if (personName != string.Empty)
opportunites = opportunites.Where(p => p.Name.StartsWith(personName));
if (personAge != 0)
opportunites = opportunites.Where(p => p.Age == personAge);
This will work fine. If personName is not given it will be not add to where, and if given then it will added.
One alternative which I have used in SQL which could be implemented in Linq too is
var p = from p in Person
where p.Name == Name_TextBox || Name_TextBox == String.Empty
select p;
(Note that your 'linq' is using SQL syntax, which won't compile. Also you can't declare a var as you are doing without directly assigning a value)
why not use the null coalescing operator? eg.
var products = from a in context.products
where a.ID == (productID ?? a.ID)
select a;
This works really well on my system

Resources