ORDER BY field when using table joins - mysql

I've got a products table that I'm trying to get to work. The query brings back results; however, it isn't actually using the ORDER BY FIELD to sort the results. It's skipping it somehow. I even tried ORDER BY FIELD(sc.id,'4','5','6'), and that didn't work either.
Is it even possible to use table_name.column in an ORDER BY FIELD()? Is there an alternative or a better method of doing this query?
$product = $db1q->query("
SELECT p.id, p.name, p.image, p.url,p.subcat as subcat, sc.id as scid,sc.name as scname
FROM Product as p
JOIN Product_Sub_Category as sc ON p.subcat = sc.id
WHERE p.visibility='1' AND find_in_set(p.id,'". $sidr['products'] ."')
ORDER BY FIELD(p.subcat,'4','5','6'), sc.sort_order ASC, p.sort_order ASC")
or die ('Unable to execute query. '. mysqli_error($db1q));
I just dumbed the query down to the basic level....
$product = $db1q->query("
SELECT id, name, image, url,subcat
FROM Product WHERE visibility='1' AND id IN ({$sidr['products']}) ORDER BY FIELD(subcat,'5','4','6','22')") or die ('Unable to execute query. '. mysqli_error($db1q));
and for some reason the order of my subcats are as follows....
3,12,23,5,5,4,4,4,4,4,22
Why wouldn't they begin with 5, 4, 6(doesn't exist), and 22? Then display 3,12, and 23 after those are first....

Simple Rextester Demo
When datatype is numeric don't compare to 'string' values
eg visibility = '1' if visibility is numeric you really shouldn't have the apostrophes around it. same in the field function given subcat.
$product = $db1q->query("SELECT id, name, image, url,subcat
FROM Product
WHERE visibility='1'
AND id IN ({$sidr['products']})
ORDER BY case when subcat in (5,4,6,22) then 0 else 1 end,
FIELD(subcat,5,4,6,22)
") or die ('Unable to execute query. '. mysqli_error($db1q));
or something like:
order by case when field(sort,'5','4','22') = 0 then (select max(sort)+1+sort from Product)
else field(sort,'5','4','22') end;
The issue with the 2nd approach is that it has to run a subquery for every record. In addition if the size of subcat/sort exceed or approach the max of int we'll run into a problem adding the values together. This problem is negated by using the 2 column sort approach in the first method.
Again, my gut feeling is that the first approach with 2 sort columns would be faster; and in my opinion easier to follow/maintain. The downfall is if the sort order defined changes then we have to change code. So... why have the order defined here... what isn't the order defined in a table; or is the order passed in as a parameter by user?

Related

Using two sets of conditions in where clause

I'm trying to get the count of items in my table where it should satisfy these conditions
status = active
type = Pre-order
date = $date_input
OR
status = active
type = Both
date = $date_input
I'm trying to use this statement but I'm pretty sure it's messed up.
SELECT COUNT(id) as count_date from date_restriction
where (date='$date_input' AND status='active' AND type='Pre-order')
OR (date='$date_input' AND status='active' AND type='Both')
I also tried this to no avail
SELECT COUNT(id) as count_date from date_restriction
where date='$date_input' AND status='active' AND type='Pre-order' OR type='Both'
Whe you have mixed AND and OR condition you need () for the or clause
SELECT COUNT(id) as count_date
from date_restriction
where date='$date_input'
AND status='active'
AND ( type='Pre-order' OR type='Both')
or instear of several or condition you could use a IN clause
AND type IN ('Pre-order', 'Both')
anyway you should avoid the use of php var in SQL you are at risk for sqlinjection .. for avoid this you should take a look at prepared statement and binding param for your db driver
Your code should work. I would write this as:
select count(*) as count_date
from date_restriction
where date = ? AND status = 'active' AND
type in ('Pre-order', 'Both');
Note: The ? is for a parameter so you are not munging the query string with input values.
If I had to guess why this isn't working, I would speculate that one or both of the dates have a time component. You might try:
where date(date) = date(?) . . .
to be sure you are matching on the date, regardless of time.
SELECT count(id) as count_date
FROM date_restriction
WHERE date='$date_input' AND status='active' AND (type='Pre-order' OR type='Both');
Here, date and status both fields are common in your case; hence, don't wrap it in parenthesis whereas, add OR condition for type field only and con-cat it with AND in WHERE clause.

Concat reults when using case in statement

I've seen this question arise and seen it answered numerous times but not exactly they way in which I need. I have the following query
SELECT DISTINCT ia.UserName, ia.FirstName, ia.LastName, ia.`Password`,
CASE ipt.Code
WHEN ipt.Code LIKE "%CORP_FILE%" THEN "ROLE_GAAP"
WHEN ipt.Code LIKE "%RR_SUMMARY%" THEN "ROLE_RR"
END
AS "role"
FROM
iCTrunk.Account ia,
iCTrunk.AccountGroup ig,
iCTrunk.asscAccountAccountGroup iaaag,
iCTrunk.asscNodeAccountGroup ianag,
iCTrunk.asscNodeProjectType ianpt,
iCTrunk.ProjectType ipt
WHERE ia.Id = iaaag.AccountId
AND iaaag.AccountGroupId = ianag.AccountGroupId
AND ianag.NodeId = ianpt.NodeId
AND ianpt.ProjectTypeId = ipt.Id
AND ia.UserName = '<some user here>'
There are 2 possible result sets from the query and most users will only return 1. The issue is that if the user is an administrator then both query results will be returned. I am trying to figure out a way to group_concat the "role" field so that I only get a single returned row. Since the row result is being read by an authentication login page it would be much easier to process a single returned result.
Thank you in advance.
Just need to add the group_concat syntax and a group by; and I moved the distinct to be in the group_concat so you only get 0-2 results in the "Role" field.
SELECT ia.UserName, ia.FirstName, ia.LastName, ia.`Password`,
GROUP_CONCAT(DISTINCT CASE ipt.Code
WHEN ipt.Code LIKE "%CORP_FILE%" THEN "ROLE_GAAP"
WHEN ipt.Code LIKE "%RR_SUMMARY%" THEN "ROLE_RR"
END) AS "role"
FROM
iCTrunk.Account ia,
iCTrunk.AccountGroup ig,
iCTrunk.asscAccountAccountGroup iaaag,
iCTrunk.asscNodeAccountGroup ianag,
iCTrunk.asscNodeProjectType ianpt,
iCTrunk.ProjectType ipt
WHERE ia.Id = iaaag.AccountId
AND iaaag.AccountGroupId = ianag.AccountGroupId
AND ianag.NodeId = ianpt.NodeId
AND ianpt.ProjectTypeId = ipt.Id
AND ia.UserName = '<some user here>'
GROUP BY ia.UserName, ia.FirstName, ia.LastName, ia.`Password`
after the "END" in the case statement and before the ')' for group_concat, you could put...
ORDER BY ipt.code ASC SEPARATOR ',' if you want the names in a consistent order with a , separator. just wanted to make sure you understand the group_concat() syntax a bit more.

How to pass data dynamically to mysql query

I have following query,
SELECT t_subject.subject, SUM( t_skilllist.skill_level ) AS total_skill, t_users.first_name,
t_skilllist.skill_level
FROM `t_skilllist`
JOIN t_subject ON t_subject.id = t_skilllist.subject_id
JOIN t_users ON t_users.id = t_skilllist.user_id
WHERE t_subject.subject = 'html'
GROUP BY t_users.first_name
ORDER BY total_skill DESC
LIMIT 0 , 30
I want to display subject and skill level for each student. But, for one subject I can do that with above query. As an example for html it works. However, I want to pass more than one subject to the query dynamically. I tried to combined subjects with AND operator but it return empty result set.
How to solve this? How to pass more than two subjects to the query? I am using PHP as server side scripting language.
You can use the IN() clause.
WHERE t_subject.subject IN ('html', 'php', 'and', 'a', 'lot', 'more')

MYSQL GROUP_CONCAT and IN

I have a little query, it goes like this:
It's slightly more complex than it looks, the only issue is using the output of one subquery as the parameter for an IN clause to generate another. It works to some degree - but it only provides the results from the first id in the "IN" clause. Oddly, if I manually insert the record ids "00003,00004,00005" it does give the proper results.
What I am seeking to do is get second level many to many relationship - basically tour_stops have items, which in turn have images. I am trying to get all the images from all the items to be in a JSON string as 'item_images'. As stated, it runs quickly, but only returns the images from the first related item.
SELECT DISTINCT
tour_stops.record_id,
(SELECT
GROUP_CONCAT( item.record_id ) AS in_item_ids
FROM tour_stop_item
LEFT OUTER JOIN item
ON item.record_id = tour_stop_item.item_id
WHERE tour_stop_item.tour_stops_id = tour_stops.record_id
GROUP BY tour_stops.record_id
) AS rel_items,
(SELECT
CONCAT('[ ',
GROUP_CONCAT(
CONCAT('{ \"record_id\" : \"',record_id,'\",
\"photo_credit\" : \"',photo_credit,'\" }')
)
,' ]')
FROM images
WHERE
images.attached_to IN(rel_items) AND
images.attached_table = 'item'
ORDER BY img_order ASC) AS item_images
FROM tour_stops
WHERE
tour_stops.attached_to_tour = $record_id
ORDER BY tour_stops.stop_order ASC
Both of these below answers I tried, but it did not help. The second example (placing the entire first subquery inside he "IN" statement) not only produced the same results I am already getting, but also increased query time exponentially.
EDIT: I replaced my IN statement with
IN(SELECT item_id FROM tour_stop_item WHERE tour_stops_id = tour_stops.record_id)
and it works, but it brutally slow now. Assuming I have everything indexed correctly, is this the best way to do it?
using group_concat in PHPMYADMIN will show the result as [BLOB - 3B]
GROUP_CONCAT in IN Subquery
Any insights are appreciated. Thanks
I am surprised that you can use rel_items in the subquery.
You might try:
concat(',', images.attached_to, ',') like concat('%,', rel_items, ',%') and
This may or may not be faster. The original version was fast presumably because there are no matches.
Or, you can try to change your in clause. Sometimes, these are poorly optimized:
exists (select 1
from tour_stop_item
where tour_stops_id = tour_stops.record_id and images.attached_to = item_id
)
And then be sure you have an index on tour_stop_item(tour_stops_id, item_id).

count(*) in mysql returning only one record

I want to count how many records from another table in the same select statement , i used Left join
and in the select statement i put count(ag.*)
see the
Example :
$q = Doctrine_Query::create()
->select("a.answer_id,a.date_added , count(ag.content_id) AS agree_count")
->from('Answer a')
->leftJoin("a.Agree ag ON a.answer_id = ag.content_id AND ag.content_type = 'answer' ")
->where('a.question_id= ? ', $questionId)
But its only returning the first record, can i Fix that? or to make another table and make it only for counting ?
You are missing a GROUP BY in your query.
More infos here.
When you don't have a GROUP BY clause, it's normal to get only one row.
Count(*) will only return one record if you don't use Group By. You are asking it to count all the records, so there can be only one result.
The count() SQL function changes how results are returned from the database - without a GROUP BY the database will only return one record, regardless of other colums in the SELECT.
if you add:
group by a.answer_id
to the end of your SQL query, that might DWYM.