I've been racking my brain all day trying to figure this one out! I'm hoping you guys can help.
I'm building a PHP system which will filter out records for 'Suppliers' within a MySQL table.
The services are stored in a table as such:
id
service
The suppliers are stored in a table as such:
id
company_name
Then after much reading, I added an additional table, which links the suppliers, to their corresponding services:
id
supplier_id
service_id
One supplier can offer many services.
When a user clicks on a filter (span tags which have jQuery on clicks, which capture the id of the service clicked), I want to display the suppliers which offer that service. When a user clicks an additional service, I want to display only the suppliers that offer both of those services. And so on...
I am struggling to establish a) is this the correct way forward, and b) what the SQL would be for it. At the moment, I pass the additional service ID to PHP using $.GET, and store a string of them all as a CSV in Session variables.
Many thanks in advance,
James
is this the correct way forward
Seems sound to me, so far.
what the SQL would be for it
To get the details of all those suppliers that supply both service id 4 and service id 7, you could first join the tables then group them by supplier and filter the resulting groups for those that contain the desired matches:
SELECT p.*
FROM suppliers p
JOIN supplier_services a ON a.supplier_id = p.id
GROUP BY p.id
HAVING SUM(a.service_id = 4)
AND SUM(a.service_id = 7)
To improve the performance of this query, you could throw in a WHERE a.service_id IN (4,7)—this would be particularly beneficial if there is an index on supplier_services(supplier_id, service_id).
Related
I'm trying to understand how to write a query to distinguish if a user has access to certain form or not. I know what I am asking here looks easy but when I tried to implement it, it was whole different thing.
Maybe I am doing it wrong.
Before starting I want to mention the two tables names in start:
sys_forms,
sys_forms_in_groups
Also I am putting a SQL Fiddle link at the end.
Here is what I am trying to do.
If you can see the picture, on top dropdown box (it's a select2 dropdown), user selects the group and it will return GroupID, on the base of which I want to populated the below datatable. (DataTable is just showing groups, it's a dummy, but it will show forms, will fix it if problem is solved)
Now here the problem arise:
I want datatables to show all the forms available in sys_forms table in datatables but in actions columns of datatables only those checkboxes/switches should show granted which are available in the selected group(Group can be selected for select2 dropdown as said before).
GroupID is the column of other table sys_forms_in_groups.
All I want is that all the forms should show in the datatables no matter what group I choose, but Actions column in table should display Granted if the group has access to that particular form.
forms_in_groups is for showing if group has access to that certain form or not. For example:
FormID GroupID
------------------------------
1 1
2 1
1 2
FormID 1 is available to both groupID 1 and 2, on other hand FormID 2 is avaialable only to GroupID 1.
Here is my SQL Fiddle.
Edit
The SQL Fiddle is not working, so putting screenshots here.
Table : sys_forms
Table : sys_forms_in_groups
I have tried this query, but it only returns forms for the selected group, where I want that all forms should show but they must show granted in Actions Columns on checkboxes/switchButtons
SELECT * FROM (`sys_forms`) INNER JOIN `sys_forms_in_groups`
ON `sys_forms_in_groups`.`FormID` = `sys_forms`.`FormID` WHERE `GroupID` = 1;
I think you're, incorrectly, trying to offload the issue onto MySQL.
It's not MySQL's job to render the forms and show whether a selected group has access to a specific form. That will fall onto your rendering of the page; using Select2 in this case.
You just want to retrieve all forms, then show whether the selected group has access to a specific form. That comes down to some Javascript that makes that check for you and displays it properly.
If you want to return all forms, the query would be:
SELECT * FROM (`sys_forms`) INNER JOIN `sys_forms_in_groups`
ON `sys_forms_in_groups`.`FormID` = `sys_forms`.`FormID`;
If you only want the forms that group 1 has access to, the query would be just as you have it:
SELECT * FROM (`sys_forms`) INNER JOIN `sys_forms_in_groups`
ON `sys_forms_in_groups`.`FormID` = `sys_forms`.`FormID` WHERE `GroupID` = 1;
I didn't do a lot of digging into Select2, so I can't really help you in that aspect.
What I can see from your database schema, an inner join is the correct choice here based on the data you are looking for. The only issue I can see is in the WHERE clause. Seems that it may try to look for GroupID in the sys_forms table, so try specifying.
SELECT * FROM (`sys_forms`) INNER JOIN `sys_forms_in_groups`
ON `sys_forms_in_groups`.`FormID` = `sys_forms`.`FormID` WHERE
`sys_forms_in_groups`.`GroupID` = 1;
Finally Problem Solved and working perfectly fine, but after solving that problem i found out there is an other issue i didnt think of, but thats another matter. xD.
However, It was a little rough way to solve but i got what i wanted.
Here is how i did it,
I first needed to update my select query to,
SELECT f.FormID
, f.FormName
, f.FormCIPath
, MAX(g.IsMenuLink) AS IsMenuLink
, GROUP_CONCAT(DISTINCT g.GroupID ORDER BY g.GroupID) AS GroupIDs
FROM `sys_forms` f
JOIN `sys_forms_in_groups` g
ON g.FormID = f.FormID
AND g.GroupID IN (1,2)
GROUP BY f.formID
Here you can see it will return all the forms which belongs to group 1 and group 2 but in a way that 1 and 2 will be in same column separated by comma.
Here how it shows now.
I am not a very complex query master so i am very much grateful to stackoverflow community to help me with the query. As i wanted to join both results to show in comma separated value.
After the MysQL the jquery work was not much difficult, i only sent the group ID for which i wanted the result to show in table. and there i got this result in return where i separate the GroupIDs with javascript split function and i get my groups.
Thankyou again everyone.
This question is about selecting data from multiple tables, joins, Doctrine2, ResultSetMapping, DQL and such stuff.
I have 4 tables:
user
contact
contact_phone
call
With relations as shown on the image: http://i.stack.imgur.com/762Jw.png
Every user can have many contacts, each contact can have many phones and each user can have many calls to/from his contacts. Just like in the real world... I've limited the number of fields in each table just for clarity.
So my problem is that I don't know how exactly to map call numbers to contact names when showing a list of calls for a specific user.
If I want to list all calls of user 1 I do:
$callRepository = $this->getDoctrine()->getRepository('MyBundle:Call');
$calls = $callRepository->findAll(array('user' => 1));
But this will give me just the list of all calls for this user and will not associate number (call.number) with names (contact.name).
I can achieve what I want with plain SQL with this query:
SELECT
c.number,
contact.name
FROM
`call` c
JOIN contact_phone cp ON
cp.number = c.number
JOIN contact ON
contact.id = cp.contact_id
WHERE
c.user_id = contact.user_id
AND c.user_id = 1
Please note that I don't want to select all calls (with SQL) and then map numbers to names with another query from the PHP layer because this way I won't be able to search the calls by name for example.
I was thinking that ResultSetMapping could help me in this case but I have no luck putting the SQL query and the ResultSetMapping together.
Please help,
Thanks!
As per my knowledge, you can acheive by using the below methods. Please go to bottom of the page. you can find Joins... try once..
http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/dql-doctrine-query-language.html
I need to keep all purchases from a single client together on a database table (It will be atored in my Orders table) and once someone is browsing a product, the system will search the dB for clients that bought that product and recommend the other products they bought (you will need to check for popularity of the other products and avoid repetition)
The data will be stored in a mysql database in a table called Orders. I then need to be able to search that database to see if other people have bought this product and if so which products they also bought.
So I've come up with this query
SELECT ProductName FROM Orders
(I have little knowledge and would like to no if I am on the right track)
I need to keep all purchases from a single client together on a database table
For this one you probably need to create a horizontal view (restricting by the client id you need to monitor the purchases) CREATE VIEW.
Could you provide your database schema ? in order to create the query you need you should join many tables, so it would be easier to provide the schema and how your tables are tied together.
try this
SELECT ProductName
FROM Orders WHERE client_id in
(SELECT client_id
FROM Orders
WHERE productname="ProductName ");
I am looking to implement a per-user tag/interest cloud feature to a website I am making.
Each user has a profile page, and on said page a tag cloud of their preselected interests will be displayed. Each user can type their interests comma delimitated, with suggestions if such a tag has been used before or creation if it doesn't exist. Interests will be things such as Music Genres, Hobbies etc.
I'd like to also add basic features such as comparing users tag clouds (shared tags) for finding users that are 'compatible' according to their cloud.
I could use help with the logistics of the database to achieve this. I understand simple database design, but I can't wrap my head around design for the above.
At the moment the database is one single table, with ID/Username/Password/Verification (the last a key for email verification).
The only idea I have come up with for the tag cloud db is two tables - one called tags with a tagid and tagname field, and another users_tags with a tagid and userid field, and an entry for every single tag a user has. However I am unsure if this is best practice.
Hope someone can give me some direction on all this - thanks in advance.
having a table with userid and tagid only sounds like the best route for this.
to find "compatible" users as you mention you can just run a query similar to
SELECT
ut.userid, COUNT(*) ct
FROM
user_tags ut
WHERE
ut.tagid IN (SELECT uta.tagid FROM user_tags uta WHERE uta.userid=24 )
GROUP BY ut.userid ORDER BY ct DESC;
note that the above query will also return the original user, but it's much more efficient than removing him from the query.
I'm building an activity stream for our site, and have made some decent headway with something that works pretty well.
It's powered by two tables:
stream:
id - Unique Stream Item ID
user_id - ID of the user who created the stream item
object_type - Type of object (currently 'seller' or 'product')
object_id - Internal ID of the object (currently either the seller ID or the product ID)
action_name - The action taken against the object (currently either 'buy' or 'heart')
stream_date - Timestamp that the action was created.
hidden - Boolean of if the user has chosen to hide the item.
follows:
id - Unique Follow ID
user_id - The ID of the user initiating the 'Follow' action.
following_user - The ID of the user being followed.
followed - Timestamp that the follow action was executed.
Currently I'm using the following query to pull content from the database:
Query:
SELECT stream.*,
COUNT(stream.id) AS rows_in_group,
GROUP_CONCAT(stream.id) AS in_collection
FROM stream
INNER JOIN follows ON stream.user_id = follows.following_user
WHERE follows.user_id = '1'
AND stream.hidden = '0'
GROUP BY stream.user_id,
stream.action_name,
stream.object_type,
date(stream.stream_date)
ORDER BY stream.stream_date DESC;
This query actually works pretty well, and using a little PHP to parse the data that MySQL returns we can create a nice activity stream with actions of the same type by the same user being grouped together if the time between the actions isn't too great (see below example).
My question is, how do I make this smarter? Currently it groups by one axis, "user" activity, when there are multiple items by a particular user within a certain timeframe the MySQL knows to group them.
How can I make this even smarter and group by another axis, such as "object_id" so if there are multiple actions for the same object in sequence these items are grouped, but maintain the grouping logic we currently have for grouping actions/objects by user. And implementing this without data duplication?
Example of multiple objects appearing in sequence:
I understand solutions to problems like this can get very complex, very quickly but I'm wondering if there's an elegant, and fairly simple solution to this (hopefully) in MySQL.
Some observations about your desired results:
Some of the items are aggregated (Jack Sprat hearted seven sellers) and others are itemized (Lord Nelson chartered the Golden Hind). You probably need to have a UNION in your query that pulls together these two classes of items from two separate subqueries.
You use a fairly crude timestamp-nearness function to group your items ... DATE(). You may want to use more sophisticated and tweakable scheme... like this, maybe
GROUP BY TIMESTAMPDIFF(HOUR,CURRENT_TIME(),stream_date) DIV hourchunk
This will let you group stuff by age chunks. For example if you use 48 for hourchunk you'll group stuff that's 0-48 hours ago together. As you add traffic and action to your system you may want to decrease the hourchunk value.
My impression is you need to group by user, as you do, but also, after that grouping, by action.
It looks to me like you need a subquery like this:
SELECT *, -- or whatever columns
SUM(actions_in_group) AS total_rows_in_group,
GROUP_CONCAT(in_collection) AS complete_collection
FROM
( SELECT stream.*, -- or whatever columns
COUNT(stream.id) AS actions_in_user_group,
GROUP_CONCAT(stream.id) AS actions_in_user_collection
FROM stream
INNER JOIN follows
ON stream.user_id = follows.following_user
WHERE follows.user_id = '1'
AND stream.hidden = '0'
GROUP BY stream.user_id,
date(stream.stream_date)
)
GROUP BY object_id,
date(stream.stream_date)
ORDER BY stream.stream_date DESC;
Your initial query (now the inner one) groups by user, but then the user groups are regrouped by identical actions - that is, identical products bought or sales from one seller would be put together.
Over at Fashiolista we've opensourced our approach to building feed systems.
https://github.com/tschellenbach/Feedly
It's currently the largest open source library aimed at solving this problem. (but written in Python)
The same team which built Feedly also offers a hosted API, which handles the complexity for you. Have a look at getstream.io There are clients for PHP, Node, Ruby and Python.
https://github.com/tbarbugli/stream-php
It also offers support for custom defined aggregations, which you are looking for.
In addition have a look at this high scalability post were we explain some of the design decisions involved:
http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic-feeds.html
This tutorial will help you setup a system like Pinterest's feed using Redis. It's quite easy to get started with.
To learn more about feed design I highly recommend reading some of the articles which we based Feedly on:
Yahoo Research Paper
Twitter 2013 Redis based, with fallback
Cassandra at Instagram
Etsy feed scaling
Facebook history
Django project, with good naming conventions. (But database only)
http://activitystrea.ms/specs/atom/1.0/ (actor, verb, object, target)
Quora post on best practises
Quora scaling a social network feed
Redis ruby example
FriendFeed approach
Thoonk setup
Twitter's Approach
We have resolved similar issue by using 'materialized view' approach - we are using dedicated table that gets updated on insert/update/delete event. All user activities are logged into this table and pre-prepared for simple selection and rendering.
Benefit is simple and fast selection, drawback is little bit slower insert/update/delete since log table has to be updated as well.
If this system is well design - it is a wining solution.
This is quite easy to implement if you are using ORM with post insert/update/delete events (like Doctrine)