rails make query better - mysql

In my table schema
I have “listeners” table connect to “devices” table (one to many)
Now I need to search on listener table but with the devices.id field
My problem is that the 2 table should have many of records (more than 1,000,000) and I afraid that my query will crash the application
My problem is on the in select
I want to know what can I do to make this query better
My full query on gist https://gist.github.com/anonymous/8623345
limit = 10
#listeners = Listener.all
#total_count = #listeners.count
#sEcho = params[:sEcho]
**#listeners =#listeners.where(" (listeners.name like ?) or (listeners.email like ?) or (countries.name like ?) or (listeners.city like ?) or (listeners.id in (select listener_id from devices where id = ?))", "%#{params[:sSearch]}%", "%#{params[:sSearch]}%" , "%#{params[:sSearch]}%","%#{params[:sSearch]}%","#{params[:sSearch]}") if params[:sSearch].present?**
order_by ="listeners.id asc"
#listeners = #listeners.includes(:devices)
#listeners = #listeners.includes(:country)
#count = #listeners.count
#listeners = #listeners.order(order_by)
#listeners = #listeners.limit(limit).offset(params[:iDisplayStart])

You need to use kaminari or will paginate for pagination and you are making this more complex this is the shortest form i think so:
Listener.includes(:devices, country).where(" (listeners.name like ?) or (listeners.email like ?) or (countries.name like ?) or (listeners.city like ?) or (listeners.id in (select listener_id from devices where id = ?))", "%#{params[:sSearch]}%", "%#{params[:sSearch]}%" , "%#{params[:sSearch]}%","%#{params[:sSearch]}%","#{params[:sSearch]}").limit(limit).offset(params[:iDisplayStart]||1).order(:id)

Related

Eloquent query building complex query to get unique records searching for an ID in 2 different columns in same table

I'm migrating a project to Laravel 4 and I am stuck with a quite complex query, which I'd like to migrate into a proper Eloquent query.
I have a table that contains chat messages, called chat_messages with a representing Model Chatmessage
The table contains a sender and a receipient column with a user id linking to the users table and User Model.
The query to get a list with all user IDs of all chat partners in raw SQL on the old version of the application is as follows:
$sql_allChatPartners = "SELECT DISTINCT chatPartner
FROM ( SELECT * FROM (
SELECT cm_receipient AS chatPartner, cm_sent_at
FROM chat_messages WHERE cm_sender = '".$me->userID."'
UNION
SELECT cm_sender AS chatPartner, cm_sent_at
FROM chat_messages WHERE cm_receipient = '".$me->userID."'
) whateva ORDER BY whateva.cm_sent_at DESC ) other";
Sorry for naming the "fake" tables whateva and other :-)
Could anyone put me in the right direction to do this with Eloquent Querybuilder?
It is important that I get the list of chatPartner IDs in the correct order, where the last chat message has been exchanged as first chatPartner. And the chatPartner where longest inactivity was in the chat as last entry.
This is what I got so far in my User Model...
public function allopenchats(){
$asSender = Chatmessage::where('sender', $this->id)->select('receipient as chatPartner, created_at');
$asBoth = Chatmessage::where('receipient', $this->id)->select('sender as chatPartner, created_at')
->union($asSender)->orderBy('created_at', 'desc')->get();
}
I renamed the columns cm_receipient to receipient, cm_sender to sender and sent_at to created_at in the new database for the new version
Your help would be very much appreciated!
You sql may change to:
SELECT IF (cm_receipient = '10', cm_sender, IF (cm_sender = '10',cm_receipient, 'no')) AS chatPartner, cm_sent_at
FROM chat_messages
WHERE cm_receipient = '10' OR cm_sender = '10'
GROUP BY chatPartner
HAVING chatPartner != 'no'
order by cm_sent_at DESC
In orm:
Chatmessage::where('sender','=',$this->id)
->orWhere('receipient','=',$this->id)
->select(DB::raw('IF (receipient = '.$this->id.', sender, IF (sender = '.$this->id.',receipient, 'no' )) AS chatPartner'), 'created_at')
->groupBy('chatPartner')
->having('chatPartner', '!=', 'no')
->orderBy('created_at', 'desc')
->get();
Thanks very much to Vitalik_74, I wouldn't have come that far without him.
Here is now the final query, although its not in ORM, but it is working fine and gives me the result I need.
$result = DB::select("SELECT *
FROM (
SELECT IF( receipient = '".$this->id."', sender, IF( sender = '".$this->id."', receipient, 'no' ) ) AS chatPartner, created_at
FROM chatmessages
WHERE receipient = '".$this->id."'
OR sender = '".$this->id."'
HAVING chatPartner != 'no'
ORDER BY created_at DESC
)whateva
GROUP BY whateva.chatPartner
ORDER BY whateva.created_at DESC");
if there is someone out there who can do this query with the Laravel Query Builder, I would be happy to see it. For now I'll leave it like this.

How to sort the results with reference to the keyword

I am developing a classifieds site. For searching product or company I am using wildcard (%) in both in front and back.
$keyword = "%".$user_keyword."%";
And I am using the following mysql query:
$query = "SELECT DISTINCT login.id_user,
login.company,
login.district,
FROM login LEFT JOIN products
ON login.id_user = products.id_user
WHERE (login.district LIKE ?
AND login.place LIKE ?)
AND
(login.company LIKE ?
OR login.summary LIKE ?
OR products.description LIKE ?)
LIMIT ?, ?";
";
This is working. But I want to sort the list with reference to the keyword.
For eg: If an user search for "foo" the following result may be get:
fool
kafoo
safoora
foo
foolan
I want to get "foo" as the first item
Please try following:
$query = "SELECT DISTINCT login.id_user,
login.company,
login.district,
FROM login LEFT JOIN products
ON login.id_user = products.id_user
WHERE (login.district LIKE ?
AND login.place LIKE ?)
AND
(login.company LIKE ?
OR login.summary LIKE ?
OR products.description LIKE ?)
order by if(LOCATE('$user_keyword', login.district)=0, 9999, LOCATE('$user_keyword', login.district))+
if(LOCATE('$user_keyword', login.place)=0, 9999, LOCATE('$user_keyword', login.place))+
if(LOCATE('$user_keyword', login.company)=0, 9999, LOCATE('$user_keyword', login.company))+
if(LOCATE('$user_keyword', login.summary)=0, 9999, LOCATE('$user_keyword', login.summary))+
if(LOCATE('$user_keyword', products.description)=0, 9999, LOCATE('$user_keyword', products.description))
LIMIT ?, ?";
";
If you just need exact match to appear as the first one in the result, then you should add something like this to your query:
[...]
ORDER BY
CASE
WHEN checked_column = 'keyword' THEN 1 /* exact match */
ELSE 2 /* non-exact match */
END,
checked_column /* sort non-exact matches */
But if you need a more sophisticated sorting like scoring your results to reflect how similar they are to a given keyword, then the concept you are looking for is called edit distance.

IF condition in mysql

I have a contact table I wish to query when a certain condition exists. I tried the query below but am getting a syntax error.
SELECT *
FROM contact_details
WHERE contactDeleted` =0
AND IF ( contactVisibility = "private"
, SELECT * FROM contact_details
WHERE contactUserId = 1
, IF( contactVisibility = "group"
, SELECT * FROM contact_details
WHERE contactGroup = 3
)
)
If I'm understanding your question correctly (which is difficult with the lack of info you've provided. Sample datasets and expected outcomes are typically helpful), then I don't believe you need IFs at all for what you want. The following will return contacts that are not deleted and who either have (visibility = "private" and userId = 1) OR (visibility = "group" and group = 3)
SELECT *
FROM contact_details
WHERE contactDeleted = 0
AND (
(contactVisibility = "public")
OR
(contactVisibility = "private" AND contactUserId = 1)
OR
(contactVisibility = "group" AND contactGroup = 3)
)
I am assuming you want to use the IF() function and not the statement which is for stored functions..
Refer to this link for more information on that.
Notice that you have put 2 select statements in there, where the custom return values are supposed to be. So you are returning a SELECT *... now notice that in your upper level sql statement you have an AND.. so you basically writing AND SELECT *.. which will give you the syntax error.
Try using .. AND x IN (SELECT *) .. to find if x is in the returned values.
Let me also list this link to make use of an existing and well written answer which may also applicable to your question.

Simplify sql query to obtain one line per id

I have a multi-table SQL query.
My need is: The query should I generate a single line by 'etablissement_id' ... and all information that I want to be back in the same query.
The problem is that this query is currently on a table where "establishment" may have "multiple photos" and suddenly, my query I currently generates several lines for the same id...
I want the following statement - LEFT JOINetablissementContenuMultimediaON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId - only a single multimedia content is displayed. Is it possible to do this in the query below?
Here is the generated query.
SELECT DISTINCT `etablissement`. * , `etablissementContenuMultimedia`. * , `misEnAvant`. * , `quartier`. *
FROM `etablissement`
LEFT JOIN `etablissementContenuMultimedia` ON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId
LEFT JOIN `misEnAvant` ON misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id
LEFT JOIN `quartier` ON quartier_id = etablissement_quartierId
WHERE (
misEnAvant_typeMisEnAvantId =1
AND (
misEnAvant_dateDebut <= CURRENT_DATE
AND CURRENT_DATE <= misEnAvant_dateFin
)
)
AND (
etablissement_isActive =1
)
ORDER BY `etablissement`.`etablissement_id` ASC
LIMIT 0 , 30
Here is the code used ZF
public function find (){
$db = Zend_Db_Table::getDefaultAdapter();
$oSelect = $db->select();
$oSelect->distinct()
->from('etablissement')
->joinLeft('etablissementContenuMultimedia', 'etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId')
->joinLeft('misEnAvant', 'misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id')
->joinLeft('quartier', 'quartier_id = etablissement_quartierId ')
->where ('misEnAvant_typeMisEnAvantId = 1 AND (misEnAvant_dateDebut <= CURRENT_DATE AND CURRENT_DATE <= misEnAvant_dateFin) ')
->where ('etablissement_isActive = 1')
->order(new Zend_Db_Expr('RAND()'));
$zSql = $oSelect->__toString();
if(isset($_GET['debug']) AND $_GET['debug'] == 1)
echo $zSql ;
//die();
$oResultEtablissement = $db->fetchAll($oSelect);
return $oResultEtablissement ;
}
Can you help me?
Sincerely,
If you are looking to have only one of the media displayed out of many regardless of which it may be then you can just add a limit to the query? After that you can tweak the query for ASCending or DESCending perhaps?
Is this query supposed to have images (or image as it were) for one establishment, or one image each for each active establishment? I see you have a limit 0,30 which means you're likely paginating....
If the result you want is a search for only one establishment, and the first image it comes to would work fine .. just use "limit 1" and you'll only get one result.
I took the time to redo the whole model of the database ... and now it works. There was no solution for a system as flawed

Complex MySQL query issue

I have a somewhat complex mySQL query I am trying to execute. I have two parameters: facility and isEnabled. Facility can have a value of "ALL" or be specific ID. isEnabled can have value of "ALL" or be 0/1.
My issue is that I need to come up with logic that can handle the following scenarios:
1) Facility = ALL AND isEnabled = ALL
2) Facility = ALL AND isEnabled = value
3) Facility = someID AND isEnabled = ALL
4) Facility = someID AND isEnabled = value
The problem is that I have several nested IF statements:
IF (Facility = 'ALL') THEN
IF (isEnabled = 'ALL') THEN
SELECT * FROM myTable
ELSE
SELECT * FROM myTable
WHERE isEnabled = value
END IF;
ELSE
IF (isEnabled = 'ALL') THEN
SELECT * FROM myTable
WHERE facility = someID
ELSE
SELECT * FROM myTable
WHERE facility = someID AND isEnabled = value
END IF;
END IF;
I would like to be able to combine the logic in the WHERE clause using either a CASE statement or Conditional's (AND/OR) but I am having trouble wrapping my head around it this morning. Currently the query is not performing as it is expected to be.
Any insight would be helpful!
Thanks
You could do this...
SELECT
*
FROM
myTable
WHERE
1=1
AND (facility = someID OR Facility = 'ALL')
AND (isEnabled = value OR isEnabled = 'ALL')
However, this yields a poor execution plan - it's trying to find one size fits all, but each combination of parameters can have different plans depending on data, indexes, etc.
This means that it is better to build the query dynamically
SELECT
*
FROM
myTable
WHERE
1=1
AND facility = someID -- Only include this line if : Facility = 'ALL'
AND isEnabled = value -- Only include this line if : isEnabled = 'ALL'
I know it can feel dirty to use dynamic queries, but this is a good corner case as to when then really can excel. I'll go find a spectacularly informative link for you now. (It's a lot to read, but it's very worth learning from)
Link : Dynamic Search