Laravel query nested relations - mysql

I have the following model schema
Products (can have many stores on table product_stores; can have many chains on table product_chains)
Stores (have one chain on column chain_id; can have many products on table product_stores; have a spatial location column)
Chains (can have many stores; can have many products on table product_chains)
I need to retrieve the products that match a certain expression (I am using MATCH (name) AGAINST), and I need to order them by the distance to the user's location. The distance to the user's location should be the minimum distance to any of the product stores, that are associated to the product through on either of the tables (product_stores, product_chains).
I achieved this for the stores that are associated to the product through the stores table, but I'm still not able to do it for the stores associated through Chains (nested).
For the direct stores I'm doing it like this:
$productList = $productList->withCount(['stores as distance' => function($query) use ($centerPoint) {
$query->select(DB::raw("coalesce(MIN( GLength( LineStringFromWKB( LineString( location, GeomFromText('" . $centerPoint . "') ) ) ) ) )"));
}])->having('distance', '<', $distance)->orderBy('distance', 'asc');
For the chains' stores I haven't reached a solution. Any ideas? I'm also wondering about ways to improve the performance on this query. Thank you in advance!

Related

Three tables - two identical, one with identifiers, MySQL - retrieve rows

I know I should probably be using a JOIN of some kind here, but I have run into problems.
The query below does seem to work - but the query takes ages to load. I am sure there must be a cleaner way to do it?
SELECT
DISTINCT SN.TermID AS id, SN.TermName AS name
FROM
wrex_termnames SN, wrex_term_registrations S, wrex_terms_summer_registrations X
WHERE
(SN.TermID = S.TermID AND S.TermPupilID = $id)
OR
(SN.TermID = X.TermID AND X.TermPupilID = $id)
ORDER BY termName DESC
The table structure is as follows:
wrex_termnames - a database with the names of the terms, i.e. 2008/09, 2009/10, 2010/11 which has fields TermID and TermName
wrex_term_registrations - a database of terms which pupils have registered for, which have fields TermID and TermPupilID
wrex_term_summer_registrations - a database of special summer terms which pupils have registered for, which also have fields TermID and TermPupilID
What I want to do is only return the names of the terms from wrex_termnames which have registrations logged against them in either wrex_term_registrations or wrex_terms_summer_registrations for the pupil I am interested in ($id).

Fulltext search on a column with data from 2 tables

I have two tables in MySql DB named as 'Patients' and 'Country'.
Patient table contains
'name','dob',postcode','address', 'country_id' etc.
Country table has
'id' and 'country_name' columns.
Now, I want the user to enter anything from a patient's name, postcode or country and get the required patient's result/data.
To achieve this, one way that I can think of is to perform the query using joins.
The other way, I wanted to ask was will it be a good approach to store the search variables i.e name, postcode and country in a column with full-text type in a way like this 'name_postcode_country' and when a user enters the search variable I perform the full-text search on the newly created column.
Or there's any other better approach that I should be considering.
It's not a good idea to hold all those info at a single column, you may use such a combination with a SELECT that JOINs the mentioned tables :
select p.name, p.dob, p.postcode, p.address,
c.country_name
from Patients p
inner join Country c
on ( p.country_id = c.id )
where ( upper(name) like upper('%my_name_string%') )
or ( upper(postcode) like upper('%my_postcode_string%') )
or ( upper(country) like upper('%my_country_string%') );
you need to use upper or lower pseudocolumns against case-sensitivity problems.

MySQL moving around strings, before and after comma

I have a mysql row that has coordinates saved in lat/long. I need it to be changed to long/lat.
I understand that this needs to be deliminated somehow and changed around.
What is the correct way to do this in MySQL?
Sample:
["26.247798956308,50.657465902192","26.247765257163,50.656210342278","26.24777327998,50.655413265762","26.247772343418,50.655399250007","26.24775833567,50.655206036067","26.247768008419,50.655097864672","26.247779429251,50.654965656351","26.247844499259,50.654613999694","26.247882692272,50.654350538671","26.247886070313,50.654253400286","26.248070301095,50.654265872269","26.24871145246,50.654291010692","26.248712355399,50.65429100802","26.248938997758,50.654292339968","26.248939900696,50.654292337296","26.249167443576,50.654292665245","26.249652369652,50.654311256915","26.24965327259,50.654311254243","26.250527203555,50.65426160469","26.251604232782,50.654185317821","26.251850689062,50.654165562357","26.252226248584,50.654138414997","26.252525072823,50.654117502871","26.252875354859,50.654092432768","26.253260873151,50.654076270464","26.253503640429,50.654024481477","26.253881809743,50.653956269108","26.253918794004,50.653941138898","26.253934151183,50.65394409752","26.254001887777,50.653956645335","26.254066915409,50.653981134594","26.254127534099,50.654016927469","26.254182158719,50.654063086371","26.254229361508,50.654118404871","26.254267909164,50.654181436626","26.254426530564,50.654433314626","26.254430159178,50.654440313548","26.254140446036,50.654870761592","26.253604791621,50.655536254578","26.253060504139,50.655991479569","26.252712653207,50.656277893161","26.252469279111,50.656453847903","26.252138350894,50.65664307698","26.25144296537,50.656971562628","26.250888157852,50.657182473989","26.250417074844,50.657288997819","26.249898133586,50.657394659791","26.248058102136,50.657466143684","26.247798956308,50.657465902192"]
A general rule (it is called DB normalization) is:
Never store multiple values in a single column!
So if you can change your table design you can do it like this (in my example we store points of user favorites - I don't know what purpose your points have):
users table
-----------
id
name
....
coordinates table
-----------------
user_id
lng
lat
If you then want to retrieve all records of user 'Tom' then you can run this query
select c.lng, c.lat
from coordinates c
join users u on u.id = c.user_id
where u.name = 'Tom'
If your row has a field containing a list of comma separated lat/long pairs, then would be better to have long/lat stored as separate fields in a new table with foreign key referencing the original table. Even better would be to use a Point field as suggested by #a_horse_with_no_name instead of lat/long separate fields.

Best database (mysql) structure for this case:

we have three types of data (tables):
Book (id,name,author...) ( about 3 million of rows)
Category (id,name) ( about 2000 rows)
Location (id,name) ( about 10000 rows)
A Book must have at least 1 type of Category (up to 3) AND a Book must have only one Location.
I need to correlate this data to get this query faster:
Select Books where Category = 'cat_id' AND Location = 'loc_id'
Select Books where match(name) against ('name of book') AND Location = 'loc_id'
Please I need some help.
Thanks
Have another table, say bookscategories, which has 'id , bookid, categoryid' as fields.
Use this to map books to categories.
Both your original queries will not be affected since the first query wants books in ONE specific category and location and the second query wants books that match a title and ONE location.
With tables this size, which I would consider medium-sized (not small, not large), it is more likely the indices which will make the difference. Create the tables correctly and join them appropriately (on the indexed primary and foreign keys) and your performance should be fine.

Mysql Storing Serialized Array

I have 3 Tables:
Regions Table which has 1500 Towns in it.
Categories Table which has 1800 categories.
Companies Table containing businesses.
What I need to do is grab a town, for example Brimingham and list in an Array which categories have businesses using our main Companies Table, so we don't have any categories stored in the array which don't have businesses in Brimingham.
The problem I have is the size of the array being stored, when I populate all the towns with the serialized array I cant even open the table to browse. See below array example:
a:9:{s:8:"Bailiffs";s:1:"1";s:20:"Business Consultants";s:1:"1";s:25:"Car Garages and Mechanics";s:1:"1";s:35:"Farming Livestock and Other Animals";s:1:"2";s:19:"Fashion Accessories";s:1:"1";s:6:"Hotels";s:1:"1";s:20:"Post Office Services";s:1:"1";s:13:"Schools State";s:1:"1";s:14:"Wood Craftsmen";s:1:"1";}
Can anyone suggest an alternative solution?
Cheers
I'd suggest a totally different approach that gets rid of the storage problem entirely, and should make your app more efficient. Storing serialized arrays full of information that can be retrieved from your database anyway is redundant and highly inefficient. The best approach here would be to normalize your data.
You should create a fourth table, perhaps called 'region_categories', which will be a simple lookup table:
CREATE TABLE region_categories (
regionId int unsigned not null,
categoryId int unsigned not null,
PRIMARY KEY(regionId,categoryId)
);
Now, instead of saving everything to an array, for each town/region you should instead populate this table with the categories that are in that town. Your data size is very small, as all you are storing is a pair of ids.
When it comes time to retrieve the categories for a given region, you just have to run a simple SELECT statement:
SELECT category.*
FROM region_categories AS rc LEFT JOIN categories AS c ON rc.categoryId=c.categoryId
WHERE rc.regionId=[whatever region you're dealing with]
Now you can iterate through your results, and you'll have all the categories for that region.