Conditionally order by another column - php

I query a database and order by a date column in the table and also by another column in another table. This is working perfectly.
SELECT * FROM myTable1 LEFT JOIN myTable2 b ON myTable1.id = b.id WHERE myTable1.id !='foo' ORDER BY myTable1.dateColumn DESC, b.column2 ASC
What I would like is to do is ORDER by myTable1.dateColumn ONLY if there is no value in b.column2 for that particular record. As you can see from my above current implementation, it will always ORDER BY myTable1.dateColumn regardless.
Stackoverflow Suggested tag for this question is mysql but in fact I am using mysqli. It makes no difference for the purpose of this question.

You should better provide some data sample and expected result, but so far you can try:
SELECT * FROM myTable1
LEFT JOIN myTable2 b
ON myTable1.id = b.id
WHERE myTable1.id !='foo'
ORDER BY CASE WHEN b.column2 IS NULL myTable1.dateColumn ELSE NULL END DESC, b.column2 ASC

You can put CASE Statements inside of an ORDER BY
SELECT *
FROM myTable1
LEFT JOIN myTable2 b
ON myTable1.id = b.id
WHERE myTable1.id != 'foo'
ORDER BY CASE WHEN b.column2 IS NULL THEN 1 ELSE 0 END ASC, myTable1.dateColumn DESC

You can use a function in the order part; something like the following:
SELECT * FROM myTable1 LEFT JOIN myTable2 b ON myTable1.id = b.id WHERE myTable1.id !='foo'
ORDER BY IF(b.column2 IS NULL,1,0),IF(b.column2 IS NULL,myTable1.dateColumn,NULL) DESC, b.column2 ASC

Related

Doctrine Query Language get Max/Latest Row Per Group

I am trying and failing to translate my relatively simple SQL statement into one that will work within Doctrine.
This is the SQL statement, which works as required when run against my database:
SELECT a.*
FROM score a
INNER JOIN (
SELECT name, MAX(score) AS highest
FROM score
GROUP BY name
) b
ON a.score = b.highest AND a.name = b.name
GROUP BY name
ORDER BY b.highest DESC, a.dateCreated DESC
Here's the DQL attempt thus far:
$kb = $em->createQuery(
"SELECT a
FROM ShmupBundle:Score a
INNER JOIN a.name ShmupBundle:Score b WITH a.score = b.score AND a.name = b.name GROUP BY b.name
WHERE a.platform='keyboard'
GROUP BY a.name
ORDER BY b.score DESC, a.dateCreated DESC"
);
Which is currently giving this error:
[Semantical Error] line 0, col 73 near 'ShmupBundle:Score': Error: Class ShmupBundle\Entity\Score has no association named name
The table itself is pretty simple:
id, name, score, platform, dateCreated
There are multiple entries with the same name, but different scores. I want to show only the "high score" per name. I've been trying on and off for a day or two now, with no luck. Can anyone point me in the right direction?
The query you are trying to do with doctrine is related to greatest-n-per-group. To use a sub query and then join with main query get things complicated to handle with doctrine. So below is the rewritten SQL version to get the same results without use of any aggregate functions:
SELECT
a.*
FROM
score a
LEFT JOIN score b
ON a.name = b.name
AND a.score < b.score
WHERE b.score IS NULL
ORDER BY a.score DESC
DEMO
To convert above query equivalent to doctrine or DQL is easy, below is the DQL version of above SQL:
SELECT a
FROM AppBundle\Entity\Score a
LEFT JOIN AppBundle\Entity\Score b
WITH a.name = b.name
AND a.score < b.score
WHERE b.score IS NULL
ORDER BY a.score DESC
Or with query builder you can write something like i have tested below with symfony 2.8 using the DEMO Schema
$DM = $this->get( 'Doctrine' )->getManager();
$repo = $DM->getRepository( 'AppBundle\Entity\Score' );
$results = $repo->createQueryBuilder( 'a' )
->select( 'a' )
->leftJoin(
'AppBundle\Entity\Score',
'b',
'WITH',
'a.name = b.name AND a.score < b.score'
)
->where( 'b.score IS NULL' )
->orderBy( 'a.score','DESC' )
->getQuery()
->getResult();
Another idea would be create a view using your query in database and in symfony create an entity put the view name in table annotation and just start calling your entity it will give the results returned by your query but this approach is not recommended just a temporary fix.
Inner Join Statement needs first argument as a table, that is a semantic error in your query.
$kb = $em->createQuery(
"SELECT a
FROM ShmupBundle:Score a
INNER JOIN ShmupBundle:Score b ON a.score = b.score AND a.name = b.name GROUP BY b.name
WHERE a.platform='keyboard'
GROUP BY a.name
ORDER BY b.score DESC, a.dateCreated DESC");
MySQL does not understand the : syntax. If ShmupBundle:Score is supposed to be a database and table, then use .. If Doctrine is supposed to replace it with something, then what does it do with it?
There can be only one GROUP BY clause, and it must be after the WHERE clause. Try removing the GROUP BY b.name.
There is no need to GROUP BY both b.name and a.name since they are equal.
use this
in class
$name = $em->getRepository('AppBundle:BlogPost')->getMaxId();
in repository you can use something like
public function getMaxId()
{
$qb = $this->createQueryBuilder('u');
$qb->select('u, MAX(id) as idMax');
return $qb->getQuery()->getSingleResult();
}
each entity come with some default repository functions either we defined or not
if we wish to add some extra functionality we do write down custom functions in repository class. like i want to add one more function getMaxId i will write down in repository to access this function.
for getting max or min from each group we can do with given query
select * from (select * from mytable order by `Group`, age desc, Person) x group by `Group
this is not good way to fetch max from each group as we need to write down sub query for that.
other than that we have Row_number() function
SELECT sd.* FROM ( SELECT sale_person_id,sale_person_name,no_products_sold,commission_percentage,sales_department,ROW_NUMBER() OVER(PARTITION BY sale_person_id ORDER BY no_products_sold DESC) rowNumber FROM sales_department_details )sd WHERE sd.rowNumber =1;
here you find out all work arounds

I want to show only that data which has date is null

I want to show only that data which date is null
Here is my query:
$Query = "SELECT att.`EmployeeCode`,emp.FullName ,emp.EmployeeID,emp.Photo,sum(TIME_TO_SEC(att.FinalTime)/60 - IFNULL(sp.TotalTimeLateMark,$TotalTimeLateMark)) OverTime
FROM `hr_tblattendancehistory` att
left join hr_tblemployee emp on att.EmployeeCode = emp.CardID
left join hr_tblspecialattendancesetting sp on sp.Date = att.Date
WHERE emp.FullName!='' and att.`Date` Between '$FromDate' and '$Todate=''' and (TIME_TO_SEC(att.FinalTime)/60 -IFNULL(sp.TotalTimeLateMark,$TotalTimeLateMark)) > 0
group by att.`EmployeeCode`,emp.Photo,emp.FullName
order by SUM(TIME_TO_SEC(att.FinalTime)/60 -IFNULL(sp.TotalTimeLateMark,$TotalTimeLateMark)) DESC";
How can I do this ?
You can use IS NULL
e.g.
SELECT * FROM YourTable WHERE DateColumn IS NULL;
In your particular case you are probably looking for IS NULL, just like Nilesh Thakkar has pointed out. But in the HAVING clause:
SELECT * FROM YourTable A
LEFT JOIN AnotherTable B on A.DateColumn=B.DateColumn
HAVING DateColumn IS NULL;
That is because the selection should happen after the join, not before.

How to use ELSE in SQL Query?

I have an SQL query to load items from my database.
$sql_query = "SELECT * FROM pagina_info
WHERE kwali='Luxuary' AND
id IN (
SELECT CASE WHEN kwali='Luxuary' THEN min(id)
ELSE max(id)
END
FROM pagina_info
GROUP BY product_name
)
AND voorraad>='1'
LIMIT ".$getal1.", ".$getal2."";
Now my question, is there a word that i can use in my ELSE state for taking the next value?
in my select i have THEN min(id) ELSE max(id), instead of max(id) i need a word or something for next value.
I can't find anything usefull hopefully anyone here can help me.
Bram
There is no key word to get the next id. But I am unsure if you mean the next id for that series of records, or the next id that will be assigned as an auto increment primary key.
Assuming the first you could use a user variable to add a sequence number, or you could join the table against itself where the 2nd copy has a larger id but matches on the product name.
Depending on your exact requirements (some table layouts and test data would help) then something like one of these would work.
Firstly, sub query being used with an IN clause
SELECT *
FROM pagina_info
WHERE kwali='Luxuary'
AND id IN
(
SELECT CASE
WHEN kwali='Luxuary' THEN min(a.id)
ELSE min(b.id)
END
FROM pagina_info a
LEFT OUTER JOIN pagina_info b
ON a.product_name = b.product_name
AND a.id < b.id
GROUP BY a.product_name
)
AND voorraad>='1'
LIMIT ".$getal1.", ".$getal2."";
Or doing a join against a sub query.
SELECT pagina_info.*
FROM pagina_info
INNER JOIN
(
SELECT CASE
WHEN kwali='Luxuary' THEN min(a.id)
ELSE min(b.id)
END AS id
FROM pagina_info a
LEFT OUTER JOIN pagina_info b
ON a.product_name = b.product_name
AND a.id < b.id
GROUP BY a.product_name
) c
ON pagina_info.id = c.id
WHERE pagina_info.kwali='Luxuary'
AND pagina_info.voorraad>='1'
LIMIT ".$getal1.", ".$getal2."";

Mysql group_concat with distinct and where gives strange results

I have the following query which works fine (see below).
But when I add a condition, for example AND (specialtyName = '...') the main results are fine, but the GROUP_CONCAT only shows the results that match the condition.
Can anyone please help me with this?
Thanks in advance.
Fred.
SELECT
tblJobs.jobID,
tblJobs.jobName,
DATE_FORMAT(tblJobs.jobDate,'%d-%m-%Y'),
tblCompanies.companyID,
tblCompanies.companyName,
tblCompanies.companyNameConvert,
GROUP_CONCAT(DISTINCT tblSpecialties.specialtyName
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
AS specialtyNames,
GROUP_CONCAT(DISTINCT tblSpecialties.specialtyNameConvert
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
AS specialtyNamesConvert,
GROUP_CONCAT(DISTINCT tblRegions.regionName),
GROUP_CONCAT(DISTINCT tblRegions.regionNameConvert)
FROM tblJobs
LEFT JOIN tblCompanies ON
(tblJobs.jobCompany = tblCompanies.companyID)
LEFT JOIN tblSpecialties ON
FIND_IN_SET(tblSpecialties.specialtyID, REPLACE(tblJobs.jobSpecialty,' ',','))
LEFT JOIN tblRegions ON
FIND_IN_SET(tblRegions.regionID, REPLACE(tblJobs.jobRegion,' ',','))
WHERE
AND jobActive = '1'
AND jobDate >= '2013-01-01'
AND companyActive = '1'
GROUP BY jobID
ORDER BY jobDate DESC, jobID DESC, jobCompany DESC
If you say:
WHERE jobActive = '1' AND jobDate >= '2013-01-01' AND companyActive = '1' AND
specialties = XXX
Then you are only going to get exactly those specialties. The filtering is done before the aggregation. As a note: including such conditions in the where clause also turns the outer joins to inner joins. Your joins are probably on properly aligned foreign key relationships, so inner joins may be appropriate.
I'm guessing what you really want is to filter jobs by those having that specialty, but to keep all other information. You want to do the filtering after the aggregation. Do this with a having clause instead of a where clause:
having sum(specialties = XXX) > 0;
This will keep only the rows that have the particular specialty, and keep all the other information.
I suppose that using aliases for your tables and subqueries could resolve your problem.
You can try something like this:
SELECT
tblJobs.jobID,
tblJobs.jobName,
DATE_FORMAT(tblJobs.jobDate,'%d-%m-%Y'),
tblCompanies.companyID,
tblCompanies.companyName,
tblCompanies.companyNameConvert,
(SELECT GROUP_CONCAT(DISTINCT ts.specialtyName
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
FROM tblSpecialties ts) AS specialtyNames ,
, ... ,
FROM tblJobs
LEFT JOIN tblCompanies ON
(tblJobs.jobCompany = tblCompanies.companyID)
LEFT JOIN tblSpecialties ON
FIND_IN_SET(tblSpecialties.specialtyID, REPLACE(tblJobs.jobSpecialty,' ',','))
LEFT JOIN tblRegions ON
FIND_IN_SET(tblRegions.regionID, REPLACE(tblJobs.jobRegion,' ',','))
WHERE
AND jobActive = '1'
AND jobDate >= '2013-01-01'
AND companyActive = '1'
GROUP BY jobID
ORDER BY jobDate DESC, jobID DESC, jobCompany DESC
I didn't tested this code, but It could help.

Combine two MYSQL SELECT queries [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to combine two Post/Category tables MYSQL SELECT queries into one
I have two MYSQL queries:
1) "SELECT ID,post_title,post_category,post_perma FROM ".TBL_POSTS."
WHERE published='1' AND page='0' ORDER BY ID ASC LIMIT 50"
2) "SELECT p.cat_ID,p.cat_nicename FROM ".TBL_CATEGORIES." n,
".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft
AND p.rgt AND n.cat_ID='".post_category."' ORDER BY p.lft
I use it like this:
$sql="SELECT ID, post_title, post_category,post_perma
FROM ".TBL_POSTS."
WHERE published='1'
AND page='0'
ORDER BY ID ASC LIMIT 50";
$result=mysql_query($sql);
while($row=mysql_fetch_assoc($result)){
$sql2="SELECT p.cat_ID, p.cat_nicename
FROM ".TBL_CATEGORIES." n, ".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft
AND p.rgt
AND n.cat_ID='".$row['post_category']."'
ORDER BY p.lft";
$result2=mysql_query($sql2);
while($row2=mysql_fetch_assoc($result2)){
$path.='/'.$row2['cat_nicename'];
}
$link.=''.$row['post_title'].'<br>';
$path='';
}
echo($link);
exit;
}
This is how I get path in the link.. now What i want is:
I want to combine both queries so, that I do not have run the second query in while loop ..
It gets very bad because if I receive 100 posts.. second query will run 100 times to fetch the path.
You can JOIN the two tables like so:
SELECT
p.ID,
p.post_title,
c.cat_nicename,
p.post_perma
FROM TBL_POSTS p
INNER JOIN TBL_CATEGORIES c
ON p.CategoryID = c.Cat_ID
WHERE p.published = '1'
AND p.page = '0'
ORDER BY p.ID ASC
LIMIT 50
Try to use MySql JOIN or UNION
You can use a UNION to combine all of them into a single query.
Really, you would need to do something like this so that all of the columns don't get combined into each other. Also, both select statements need to have the same number of columns being selected.
EDIT: Also, in MySQL UNION's you can only have 1 order by and it has to be in the last SELECT, so you will have to adjust your query accordingly.
(SELECT ID,
post_title,
post_category,
post_perma,
NULL AS cat_id,
NULL AS cat_nicename
FROM ".TBL_POSTS."
WHERE published='1'
AND page='0')
ORDER BY ID ASC LIMIT 50
UNION
(SELECT
NULL AS ID,
NULL AS post_title,
NULL AS post_category,
NULL AS post_perma,
p.cat_ID,
p.cat_nicename
FROM ".TBL_CATEGORIES." n,
".TBL_CATEGORIES." p
WHERE n.cat_ID='".post_category."'
AND n.lft BETWEEN p.lft AND p.rgt)
ORDER BY p.lft

Categories