Search Logic to access more than 6 related tables efficient - php

in my current project, the task is to be able to search through 6 tables.
Tables are:
A, B, C, D, E, F
All tables are related to each other through another table in the list.
E.g. A->B B->A->C C->F F->D
Like User->Phonenumber Phonenumber->Country etc.
Since the searchmask/form the user gets presented leaves the possibility open to send only certain parameters to the server it occurs that the search must find a result R no matter which parameter the user passes through.
In this case a parameter is a column value of any of the above listed tables, e.g. Phonenumber->Provider = Verizon means, the user wants all entries with the provider = Verizon.
The actual problem is not searching in specific tables, it is searching in specific tables an retrieving the right result from the results you found in the specific tables.
Let me explain: Result, in our case is NOT a table object of the table we currently searched through.
A Result should always be the same object Result which must have at least one entry in A and B.
Example Requirements:
A = User exists
B = he has a Phonenumber
Since all tables have relations to each other directly or indirectly we could access a result without searching for columns in A or B only by looking into the related tables retrieving the rows with Provider=Verizon and JOINing on the related tables via ids/keys/constraints.
Currently I do more than one query.
I first check the type of the parameters.
"Are they columns from A to C"
Yes, then do a direct SELECT on A, B or C and take the result as Result.
"Are they columns from E"
Yes, then do a SELECT on E, after that another SELECT on C with c_id from E.
As Result (in PHP) I always want to have an object/array containing A.value1, B.value1
I never want any information about C, D, E or F.
I only want to get Result consisting of A.value1 and B.value1 by any allowed/searchable parameter that is represented in a column in one or more of the tables, e.g. Provider=Verizon AND Country=China AND OnVaccation=True
I am sorry for the complexity.

I think, for this you could use Sphinx Search Engine at http://sphinxsearch.com/ . You should set up SQL query which will be used for building search index. After this from PHP you could use simple API for performing search.

Related

Join tables vs JSON data in MySQL

I am wondering which is the best approach to handle relationships in my data.
My person table currently stores 'name' and 'url' for multiple instances of the following (column names changed for simplicity):
Favourite books
Favourite websites
Favourite shops
Favourite places
So, for each of the above, I have a column in the person table containing a JSON string. For example, for a single person, I might have the following in the 'favourite_shops' column:
[{"name":"john lewis", "url":"http://www.johnlewis.com"},{"name":"tesco", "url":"http://www.tesco.co.uk"}]
And the following in the 'favourite_websites' column
[{"name":"bbc", "url":"http://www.bbc.co.uk"},{"name":"the guardian", "url":"http://www.guardian.co.uk"}]
Obviously these JSON strings can contain many more items, and this is repeated for the other two columns - 'favourite_books' and 'favourite_places'.
So, one query to the database can return all the data I need. But I must then loop through the JSON in AngularJS for display on the front end.
My question is this - would it be beneficial (especially due to the front end processing required), to have 4 join tables instead of the columns listed above?
Or perhaps a single jointable (as the 4 share the same properties, name and url) with an additional column indicating which of the 4 the row refers to?
Things to add:
I am using Laravel 5.2 and Eloquent
It is very unlikely I will ever need to display all 'persons' AND their related data at once, the view will be one person at a time.
It is also very unlikely I would ever need to query specific items - ie 'favourite_shops' named 'john_lewis' - the data is supplementary to a person.
The relationship between person and the 4 options is one to many - persons will not need to share the data, any matching entries are simply additional rows. The actual columns I am using are highly unlikely to have need to share data between persons.
I am building the above as a RESTful api, so all data will be json_encoded.

Good practice for handling naturally JOINed results across an application

I'm working on an existing application that uses some JOIN statements to create "immutable" objects (i.e. the results are always JOINed to create a processable object - results from only one table will be meaningless).
For example:
SELECT r.*,u.user_username,u.user_pic FROM articles r INNER JOIN users u ON u.user_id=r.article_author WHERE ...
will yield a result of type, let's say, ArticleWithUser that is necessary to display an article with the author details (like a blog post).
Now, I need to make a table featured_items which contains the columnsitem_type (article, file, comment, etc.) and item_id (the article's, file's or comment's id), and query it to get a list of the featured items of some type.
Assuming tables other than articles contain whole objects that do not need JOINing with other tables, I can simply pull them with a dynamicially generated query like
SELECT some_table.* FROM featured_items RIGHT JOIN some_table ON some_table.id = featured_items.item_id WHERE featured_items.type = X
But what if I need to get a featured item from the aforementioned type ArticleWithUser? I cannot use the dynamically generated query because the syntax will not suit two JOINs.
So, my question is: is there a better practice to retrieve results that are always combined together? Maybe do the second JOIN on the application end?
Or do I have to write special code for each of those combined results types?
Thank you!
a view can be thot of as like a table for the faint of heart.
https://dev.mysql.com/doc/refman/5.0/en/create-view.html
views can incorporate joins. and other views. keep in mind that upon creation, they take a snapshot of the columns in existence at that time on underlying tables, so Alter Table stmts adding columns to those tables are not picked up in select *.
An old article which I consider required reading on the subject of MySQL Views:
By Peter Zaitsev
To answer your question as to whether they are widely used, they are a major part of the database developer's toolkit, and in some situations offer significant benefits, which have more to do with indexing than with the nature of views, per se.

how do i pattern a php database call?

specs: PHP 5 with mySQL built on top of Codeigniter Framework.
I have a database table called game and then sport specific tables like soccerGame and footballGame. these sport specific tables have a gameId field linking back to the game table. I have corresponding classes game and soccerGame/footballGame, which both extend game.
When I look up game information to display to the user, I'm having trouble figuring out how to dynamically link the two tables. i'm curious if it's possible to get all the information with with one query. The problem is, I need to query the game table first to figure out the sport name.
if that's not possible, my next thought is to do it with two queries. have my game_model query the game table, then based off the sport name, call the appropriate sport specific model (i.e. soccer_game_model) and get the sport specific info.
I would also pass the game object into the soccer_model, and the soccer_model would use that object to build me a soccerGame object. this seems a little silly to me because i'm building the parent object and then giving it to the extending class to make a whole new object?
thoughts?
thanks for the help.
EDIT:
game table
gameId
sport (soccer, basketball, football, etc)
date
other data
soccerGame table
soccerGameId
gameId
soccer specific information
footballGame table
footballGameId
gameId
football specific information
and so on for other sports
So I need to know what the sport is before I can decide which sport specific table I need to pull info from.
UPDATE:
Thanks all for the input. It seems like dynamic SQL is only possible through stored procedures, something I'm not well versed on right now. And even with them it's still a little messy. Right now I will go the two query route, one to get the sport name, and then a switch to get the right model.
From the PHP side of things now, it seems a little silly to get a game object, pass it to, say, my soccer_game_model, and then have that return me a soccer_game object, which is a child of the original game. Is that how it has to be done? or am I missing something from an OO perspective here?
To extend on Devin Young's answer, you would achieve this using Codeigniter's active record class like so:
public function get_game_by_id($game_id, $table)
{
return $this->db->join('game', 'game.id = ' . $table . '.gameId', 'left')
->where($table . '.gameId', $game_id)
->get('game')
->result();
}
So you're joining the table by the gameId which is shared, then using a where clause to find the correct one. Finally you use result() to return an array of objects.
EDIT: I've added a second table paramater to allow you to pass in the name of the table you can join either soccerGame, footballGame table etc.
If you don't know which sport to choose at this point in the program then you may want to take a step back and look at how you can add that so you do know. I would be reluctant to add multiple joins to all sport tables as you''ll run into issues down the line.
UPDATE
Consider passing the "sport" parameter when you look up game data. As a hidden field, most likely. You can then use a switch statement in your model:
switch($gameValue) {
case 'football': $gameTable = "footballGame"; break;
case 'soccer': $gameTable = "soccerGame"; break;
}
Then base your query off this:
"SELECT *
FROM ". $gameTable . "
...etc
You can combine the tables with joins. http://www.w3schools.com/sql/sql_join.asp
For example, if you need to get all the data from game and footballGame based on a footballGameId of 15:
SELECT *
FROM footballGame a
LEFT OUTER JOIN game b ON a.id = b.gameId
WHERE footballGameId = 15
Check this Stack Overflow answer for options on how to do it via a standard query. Then you can turn it into active record if you want (though that may be complicated and not worth your time if you don't need DB-agnostic calls in your app).
Fow what it's worth, there's nothing wrong with doing multiple queries, it just might be slower than an alternative. Try a few options, see what works best for you and your app.

MySQL query - getting a list of 'extra' information from JOIN table similar to a nested array

This was a bit difficult to explain in the title, but I should be able to here. I have two tables that look like this:
Table 1:
-id
-created
-last_modified
-title
Table 2:
-id
-parent_id
-type
-value
The structure is somewhat akin to the following: an item from table one can have many attributes associated with it. Each attribute is listed in the second table, with a reference back to the original.
The issue I have, is that I want to be able to get a list of records from table 1 to display in a table (using pagination), but also want to be able to retrieve all the attributes from Table 2 associated with each Table 1 record at the same time, so that I might have the following:
(Table 1) ID1 [Title] has attributes x, y, z
(Table 1) ID2 [Title] has attributes x, y, z
(Table 1) ID3 [Title] has attributes x, y, z
and so on. Ideally I would like to be able to associate each attribute with its type as well...currently with a join I receive multiple rows of the same records (with the joined data different each time), and grouping them together removes some of the joined data entirely.
Essentially what I'm after is an array of attributes to be returned for each record from Table 1 (in some sort).
I'm thinking of using MongoDB for this project as I know I can do it simply with that, but I'm trying to do it with MySQL as that is what the existing platform is using.
I hope I've made sense with what I'm asking :) Any help would be appreciated!
Dan
Sounds like more of a display problem. The joined query is the best way to go. You'd then just have a simple loop in your retrieve/display code to check for when you transition from one Table1 record to another and adjust the output as necessary.
You could retrieve all the child records as single fields using MySQL's group_concat() function, but then you just end up with (basically) a monolithic string of concatenated data, and not the individual records the joinedquery/display loop will provide. group_concat also has length-limits on how much data it'll return (1024 bytes by default), which can be easily hit with large data sets.

Suggestions on retrieving related data across databases with different logins?

I have an array of user ids in a query from Database A, Table A (AA).
I have the main user database in Database B, Table A (BA).
For each user id returned in my result array from AA, I want to retrieve the first and last name of that user id from BA.
Different user accounts control each database. Unfortunately each login cannot have permissions to each database.
Question: How can I retrieve the firsts and lasts with the least amount of queries and / or processing time? With 20 users in the array? With 20,000 users in the array? Any order of magnitude higher, if applicable?
Using php 5 / mysql 5.
As long as the databases are on the same server just use a cross database join. The DB login being used to access the data will also need permissions on both databases. Something like:
SELECT AA.userID, BA.first, BA.last
FROM databasename.schema.table AA
INNER JOIN databasename.schema.table BA ON AA.userID = BA.userID
In response to comments:
I don't believe I read the part about multiple logins correctly, sorry. You cannot use two different mySQL logins on one connection. If you need to do multiple queries you really only have three options. A) Loop through the first result set and run multiple queries. B) Run a query which uses a WHERE clause with userID IN (#firstResultSet) and pass in the first result set. C) Select everything out of the second DB and join them in code.
All three of those options are not very good, so I would ask, why can't you change user permissions on one of the two DBs? I would also ask, why would you need to select the names and IDs of 20,000 users? Unless this is some type of data dump, I would be looking for a different way to display the data which would be both easier to use and less query intensive.
All that said, whichever option you choose will be based on a variety of different circumstances. With a low number of records, under 1,000, I would use option B. With a higher number of records, I would probably use options C and try to place the two result sets into something that can be joined (such as using array_combine).
I think they key here is that it should be possible in two database calls.
Your first one to get the id's from database A and the second one to pass them to database B.
I don't know mysql, but in sqlserver I'd use the xml datatype and pass all of the ids into a statement using that. Before the xml datatype I'd have built up some dynamic SQL with the id's in an IN statement.
SELECT UserId FROM DatabaseA.TableA
Loop through id's and build up a comma separated string.
"SELECT FirstName, Surname FROM DataBaseB.TableA WHERE UserId IN(" + stringId + ")"
The problem with this is that wth 20,000 id's you may have some performance issues with the amount of data you are sending. This is where'd I'd use the XML datatype, so maybe look at what alternatives mysql has for passing lists of ids.

Categories