Ordering mysql query results by location - mysql

I've a search bar to search for other users by their name or username, and that's easy to do, but what I'm trying to do is that in case more that one user have the same name that you're searching for I want to show first the closest one to you, so that first will appear the users with that name who are in the same city as u, then the same country, and then the rest of the world, I'am able to achieve the required result with multiple queries, but is it achievable with one query?
NOTE: the user table that I'm using for the search contains Username, FName, LName, CountryCode, CityID.
these are the queries I'm using now:
Select user.USERNAME, AVG(userrating.RATING) as Avg_Rating
from user LEFT JOIN userrating on user.USERNAME = userrating.USERNAME
WHERE CONCAT (user.FNAME, " ", user.LNAME) like '%Searched Name%' and user.CITYID = User's_City_ID
GROUP by user.USERNAME
ORDER by Avg_Rating
then I use the same query but for the country of the user and excluding the previously used city:
Select user.USERNAME, AVG(userrating.RATING) as Avg_Rating
from user LEFT JOIN userrating on user.USERNAME = userrating.USERNAME
WHERE CONCAT (user.FNAME, " ", user.LNAME) like '%Searched Name%' and user.CountryCode = User's_Country_Code and not user.CITYID = User's_City_ID
GROUP by user.USERNAME
ORDER by Avg_Rating
and then the same but excluding the whole country that I used in the prev. query:
Select user.USERNAME, AVG(userrating.RATING) as Avg_Rating
from user LEFT JOIN userrating on user.USERNAME = userrating.USERNAME
WHERE CONCAT (user.FNAME, " ", user.LNAME) like '%Searched Name%' and not user.CountryCode = User's_Country_Code
GROUP by user.USERNAME
ORDER by Avg_Rating
and then I'm combining the results of the three queries.

Starting from your current query (called « other » in the below SQL), you want to JOIN on the record that corresponds to the current user (called « me ») and then use a special ORDER BY clause to show the closest matching records first, using a CASE construct.
This assumes that « username » can be used to uniquely identify the current user.
SELECT
other.username
FROM userrating other
INNER JOIN userrating me on me.username = 'bar'
WHERE
CONCAT (other.FNAME, " ", other.LNAME) like '%Searched Name%'
ORDER BY
CASE
WHEN other.cityid = me.cityid THEN 0
WHEN other.countryid = me.countryid THEN 1
ELSE 2
END,
other.username
You need to replace 'bar' with current username.

It's difficult to do this exactly with one query, but you can do something like this:
SELECT * FROM users WHERE username LIKE '%searchkey%' OR fname LIKE '%searchkey%' OR lname LIKE '%searchkey%' OR countryid LIKE '%searchkey%' OR cityid LIKE '%searchkey%'
Here you can read more about the LIKE Operator.

Related

Group concat function in mysql dont give results

i want the user_name from a table by joining user_m to approval_master.
The user_name in approval_master is in the form of string
like
'abcd1234','pqrs1234'.
I want the names of those users
like
abcd,pqrs
The usernames are in a column called LEVEL_1.
This is my query
select GROUP_CONCAT(DISTINCT u.Name), am.id, c.DEPARTMENT_DESC
from approval_master am join
cost_center_lov c
on c.DEPARTMENT_ID = am.DEPARTMENT join
user_m u
on FIND_IN_SET(u.User_Name, am.LEVEL_1)
group by am.id
The result of query
select LEVEL_1 from approval_master is
'md2188','admin'
'md2188'
The result of query
select user_name, name from user_m is
user_name name
-----------------
Admin Admin
md2188 MD
I want my result to be
MD,Admin
MD
Result: Nothing!!
Make sure your FIND_IN_SET function is returning data. Take out the group_concat function from the query, and just search for "select distinct u.name, am.id, c.department_desc...."

Mysql query that searches in table with no reference to current search?

I have a HTML input field that searches for names in a MySql table and drops down a DIV that displays first 15 records found.
The names are displayed with DISTINCT (so unique values).
If I type in for example adr in the text input box, it will display first 15 matches for "%adr%".
What I need to do now (and I can't get any luck with it), is look for 2 different types of codes (located in codes_table_1 and codes_table_2), in the same text input field (so expanding my existing query).
The structure of the 2 codes_tables would be this below (each having id and name as columns):
code_id_1 | code_name_1
1 XK112932
2 XK082192
3 ...
and
code_id_2 | code_name_2
1 GG882931
2 GG014253
3 ...
So I need to find a way to add to my DISTINCT rows containing unique persons with their phone and addresses, these codes, as in adding new rows to the result query.
For example if instead of a name I look for "KD139", I need to get 1st 15 occurences that begin / or have inside the code KD139, looking this up into the 2 codes tables.
Here's my query for unique names :
SELECT DISTINCT CONCAT(COALESCE(customer.last_name,''), ' ',
COALESCE(customer.first_name,'')) as fullName,
customer.first_name,
customer.last_name,
customer.email,
address.phone,
customer.customer_id
FROM customer
LEFT JOIN address
ON address.address_id = customer.address_id
WHERE CONCAT(COALESCE(customer.first_name,''), ' ', COALESCE(customer.last_name,'')) LIKE :search OR
CONCAT(COALESCE(customer.last_name,''), ' ', COALESCE(customer.first_name,'')) LIKE :search OR
customer.business_name LIKE :search OR
customer.email LIKE :search OR
address.phone LIKE :search
LIMIT 15
UPDATE (a solution that works!) :
(SELECT DISTINCT
customer.customer_id,
CONCAT(COALESCE(customer.last_name,''), ' ', COALESCE(customer.first_name,'')) as fullName,
customer.email,
address.phone
FROM customer
LEFT JOIN address
ON address.address_id = customer.address_id
INNER JOIN th_table
WHERE CONCAT(COALESCE(customer.first_name,''), ' ', COALESCE(customer.last_name,'')) LIKE :search OR
CONCAT(COALESCE(customer.last_name,''), ' ', COALESCE(customer.first_name,'')) LIKE :search OR
customer.business_name LIKE :search OR
customer.email LIKE :search OR
address.phone LIKE :search
ORDER BY fullName ASC
LIMIT 15)
UNION ALL
(SELECT code1_table.code1_id,
code1_table.code1_number,
'TH_TABLE' as email,
Null as phone
FROM code1_table
WHERE code1_table.code1_number LIKE :search
ORDER BY code1_table.code1_number ASC
LIMIT 15)
UNION ALL
(SELECT code2_table.code2_id,
code2_table.code2_number,
'CODE2_TABLE' as email,
Null as phone
FROM code2_table
WHERE code2_table.code2_number LIKE :search
ORDER BY code2_table.code2_number ASC
LIMIT 15)
Let's say you want to search artist and song tables together for search terms. You might try someting like this.
SELECT *
FROM (
SELECT 'artist' as type,
first as first, last as last, label as tag
FROM artist
WHERE first LIKE '%searchterm%'
OR last LIKE '%searchterm%'
OR label LIKE '%searchterm%'
UNION ALL
SELECT 'song' as type,
title as first, '' as last, '' as tag
FROM song
WHERE title LIKE '%searchterm%'
)
This gets you a result set with information from both tables in it, formatted so UNION ALL makes sense. You can then filter and order the results appropriately for your application.
I offer only one guarantee on this sample code: It will be slow. LIKE '%thing' is a notorious performance antipattern.

MySQL excutes select after where clause

This query gives an error unknown column company in where clause. I found that where clause runs first and select runs next. That could be the error here. But i dont know how to correct this in order to get company in result set.
SELECT trnsdriverid,
name,
(SELECT transcompany.name
FROM transcompany,
transcompdriver
WHERE transcompany.trnscompid = transcompdriver.trnscompid) AS 'company',
address,
dob,
license,
telephone
FROM transcompdriver
WHERE ? LIKE 'All'
OR name LIKE '%"+keyword+"%'
OR company LIKE '%"+keyword+"%'
OR trnsdriverid LIKE '%"+keyword+"%'
You can't reference column aliases in where statements. You should rewrite this query to use a JOIN and then do your filtering on the actual TransCompany.name column, for example:
select
d.trnsDriverID
,d.name
,c.name as [Company]
,d.address
,d.dob
,d.license
,d.telephone
from
TransCompDriver d
join
TransCompany c
on
c.trnscompid = d.trnscompid
where
? = 'All'
or
d.name like '%" + keyword + "%'
or
c.name like '%" + keyword + "%'
or
d.trnsDriverID like '%" + keyword + "%'
Also, don't use LIKE where a simple equality operator would do. I changed the query above to use = 'All'.
You should really be doing this using a join rather than a subselect. I would recommend this:
SELECT
d.trnsDriverID,
d.name,
c.name AS `company`,
d.address,
d.dob,
d.license,
d.telephone
FROM
TransCompDriver AS d
INNER JOIN TransCompany AS c
ON d.trnsCompID = c.trnsCompID
WHERE
? like 'All'
OR d.name LIKE '%"+keyword+"%'
OR `company` LIKE '%"+keyword+"%'
OR d.trnsDriverID LIKE '%"+keyword+"%'
The sub-query that pulls the column "company" does not have matching number of rows, try the join statement instead
select trnsDriverID, name, t1.name AS company, address, dob, license, telephone
from TransCompDriver JOIN (select trnsDriverID, name,
(select TransCompany.name from TransCompany LEFT JOIN TransCompDriver
ON TransCompany.trnsCompID=TransCompDriver.trnsCompID) AS t1
where ? like 'All' or name like '%"+keyword+"%' or company like '%"+keyword+"%'
or trnsDriverID like '%"+keyword+"%'

MySQL select join order by

I have two tables:
tb_user with these fields:
userId, lastName, firstName and other fields.
tb_application with these fields:
ApplicationID, ApplicantID, applicationType, applicationStatus, applicationCycle and other fields.
Using this statement I get the recordset of the applications ordered by ApplicationID.
SELECT tb_application.ApplicationID, tb_application.ApplicantID,
tb_application.applicationType, tb_application.applicationCycle,
tb_application.applicationStatus
WHERE applicationCycle = '10' and applicationType ='5' and and applicationStatus ='1'
ORDER BY tb_application.ApplicationID
Then, I use the field ApplicantID from the applications table to retrieve the name from the users table.
But what I need to have is the list of applications ordered by Last name.
After receiving the answer from Raphael and thanks to his diligence and introducing me to the power of the "JOIN" instruction in MySQL, I modify his answer and the one that works for me is this:
SELECT * FROM tb_application
INNER JOIN tb_user ON tb_application.ApplicantID=tb_user.userId
WHERE applicationCycle = '10'
and applicationType='5'
and applicationStatus='1'
ORDER BY lastName
SELECT
--u.lastName,
tb_t.ApplicationID,
t.ApplicantID,
t.applicationType,
t.applicationCycle,
t.applicationStatus
FROM tb_application t
INNER JOIN tb_user u
ON t.ApplicantID = u.userId
WHERE
applicationCycle = '10'
AND
applicationType ='5'
AND
applicationStatus ='1'
ORDER BY u.lastName
You can comma-sparate multiple fields to sort after, like this
ORDER BY tb_application.ApplicationID, tb_user.lastName
This means that it will first sort after tb_application.ApplicationID, and within that range it will sort after tb_user.lastName

Mysql joining multiple totals - making query efficient

I am creating a clock-in / clock-out system for employees.
There is a tbl_clockins which contains records of each clock-in/clock-out session with information on whether each session is paid, how late the employee was for that session or how much overtime they did, etc.
There is another table called tbl_user_work_settings where the manager can set which days employees are on holiday, or have taken off on sickness etc.
I am creating some reports where I need totals for each employee, e.g. total days taken as holiday by each employee wihin a given date range. I have a very long query which actually gets all the required information, but it is huge and somewhat inefficient. Is there any way to make it smaller/more efficient? Any help is appreciated.
// get total days worked, unpaid days, bank holidays, holidays, sicknesses
// and absences within given date range for given users
$sql = "SELECT us.username, daysWorked, secondsWorked,
unpaidDays, bankHolidays, holidays, sicknesses, absences
FROM
(SELECT username FROM users WHERE clockin_valid='1') us
LEFT JOIN (
SELECT username, selectedDate, count(isUnpaid) AS unpaidDays
FROM tbl_user_work_settings
WHERE isUnpaid = '1'
AND selectedDate>='$startDate'
AND selectedDate<='$endDate'
GROUP BY username
) u ON us.username=u.username
LEFT JOIN (
SELECT username, count(isBankHoliday) AS bankHolidays
FROM tbl_user_work_settings
WHERE isBankHoliday='1'
AND selectedDate>='$startDate'
AND selectedDate<='$endDate'
GROUP BY username
) bh ON us.username=bh.username
LEFT JOIN (
SELECT username, count(isHoliday) AS holidays
FROM tbl_user_work_settings
WHERE isHoliday='1'
AND selectedDate>='$startDate'
AND selectedDate<='$endDate'
GROUP BY username
) h ON us.username=h.username
LEFT JOIN (
SELECT username, count(isSickness) AS sicknesses
FROM tbl_user_work_settings
WHERE isSickness='1'
AND selectedDate>='$startDate'
AND selectedDate<='$endDate'
GROUP BY username
) s ON us.username=s.username
LEFT JOIN (
SELECT username, count(isOtherAbsence) AS absences
FROM tbl_user_work_settings
WHERE isOtherAbsence='1'
AND selectedDate>='$startDate'
AND selectedDate<='$endDate'
GROUP BY username
) a ON us.username=a.username
LEFT JOIN (
SELECT username, count(DISTINCT DATE(in_time)) AS daysWorked,
SUM(seconds_duration) AS secondsWorked
FROM tbl_clockins
WHERE DATE(in_time)>='$startDate'
AND DATE(in_time)<='$endDate'
GROUP BY username
) dw ON us.username=dw.username";
if(count($selectedUsers)>0)
$sql .= " WHERE (us.username='"
. implode("' OR us.username='", $selectedUsers)."')";
$sql .= " ORDER BY us.username ASC";
You can use SUM(condition) on a single use of the tbl_user_work_settings table:
// get total days worked, unpaid days, bank holidays, holidays, sicknesses
// and absences within given date range for given users
$sql = "
SELECT users.username,
SUM(ws.isUnpaid ='1') AS unpaidDays,
SUM(ws.isBankHoliday ='1') AS bankHolidays,
SUM(ws.isHoliday ='1') AS holidays,
SUM(ws.isSickness ='1') AS sicknesses,
SUM(ws.isOtherAbsence='1') AS absences,
COUNT(DISTINCT DATE(cl.in_time)) AS daysWorked,
SUM(cl.seconds_duration) AS secondsWorked
FROM users
LEFT JOIN tbl_user_work_settings AS ws
ON ws.username = users.username
AND ws.selectedDate BETWEEN '$startDate' AND '$endDate'
LEFT JOIN tbl_clockins AS cl
ON cl.username = users.username
AND DATE(cl.in_time) BETWEEN '$startDate' AND '$endDate'
WHERE users.clockin_valid='1'";
if(count($selectedUsers)>0) $sql .= "
AND users.username IN ('" . implode("','", $selectedUsers) . "')";
$sql .= "
GROUP BY users.username
ORDER BY users.username ASC";
By the way (and perhaps more for the benefit of other readers), I really hope that you are avoiding SQL injection attacks by properly escaping your PHP variables before inserting them into your SQL. Ideally, you shouldn't do that at all, but instead pass such variables to MySQL as the parameters of a prepared statement (which don't get evaluated for SQL): read more about Bobby Tables.
Also, as an aside, why are you handling integer types as strings (by enclosing them in single quote characters)? That's needless and a waste of resource in MySQL having to perform unnecessary type conversion. Indeed, if the various isUnpaid etc. columns are all 0/1, you can change the above to remove the equality test and just use SUM(ws.isUnpaid) etc. directly.
Put each table that would join in a temp table...
then create indexes on joinable fields of temp tables...
and make your query with temp tables.
Example:
SELECT username, selectedDate, count(isUnpaid) AS unpaidDays
INTO #TempTable1
FROM tbl_user_work_settings
WHERE isUnpaid = '1'
AND selectedDate>='$startDate'
AND selectedDate<='$endDate'
GROUP BY username
create clustered index ix1 on #TempTable1 (username)