MYSQL: Select entries in relation to multiple rows of another table - php

I have two tables in a database:
entries (ID, distance, status[,...])
entriesmeta (ID, entry_ID, metakey, metavalue)
Until now, I select entries like this:
SELECT ID,
distance
FROM entries
WHERE status = '2' HAVING distance < 30
ORDER BY distance
LIMIT 20
Now, the second table contains data of this sort:
ID | entry_ID | metakey | metavalue
1 | 137 | service | 13
2 | 137 | service | 7
3 | 137 | service | 76
4 | 84 | service | 23
etc.
The entries in the first table are restaurants, the services in the second table are services they offer. What I have been trying for a while now is this: Create a HTML search form that lets users choose certain services via checkboxes. The MYSQL query then should select all entries that offer ALL those services. So far, the search form puts the selected services in an array (7,13,9,27).
My problem is finding the correct database query. It should match the metavalue-field with the checkbox-array and then select all entries from the first table, which offer all those services. I have been experimenting with "LEFT JOIN ON entries.ID = entriesmeta.entry_ID" and several WHERE clauses, but no success. I wonder if someone can help.
Thank you in advance - and sorry for my bad English.

SELECT e.ID
FROM entries e
INNER JOIN entriesmeta m
ON e.ID = m.entry_id
WHERE
m.metakey = 'service' AND
m.metavalue IN (7,13,9,27)
GROUP BY e.ID
HAVING COUNT(DISTINCT m.metavalue) = 4
SQLFiddle Demo
This query will display all ID that has metakey of service and a value(s) of 7, 13, 9, 27.

This would get you all entries which have a given entriesmeta service value - which is what I think you are asking.
SELECT entries.* from entriesmeta
INNER JOIN entries on entries.ID = entriesmeta.entry_ID
WHERE entriesmeta.metavalue = <myValue>

Related

My current MySQL join is not working

I have the following tables:
Users
id | name
-----------------
1 | Johny Bravo
Orders
id | users_id | number
----------------------
1 | 1 | 111111
2 | 1 | 222222
3 | 1 | 333333
4 | 1 | 444444
Example
id | text | number
------------------
1 | test | 111111
2 | test | 111111
3 | test | 222222
4 | test | 222222
5 | test | 333333
6 | test | 333333
Desired Outcome
id: 1
name: Johny Brawo
count(orders): 4
count(example): 6
My current query, which doesn't work
SELECT users.id, users.name, count(orders.id), count(example.id)
FROM users
LEFT JOIN orders ON orders.users_id=users.id
LEFT JOIN example ON example.number=orders.number
GROUP BY users.id
My current result
id: 1
name: Johny Brawo
count(orders): 8
count(example): 8
Any help would be greatly appreciated.
try count(distinct orders.id), count(distinct example.id)
I've not done any MySQL really, but this works in other Databases...
Starting - a little bit of theory. What does your query do?
First it SELECTs something from users table.
Then it LEFT JOINs with orders table. Number of returned rows is a multiplication of rows from users table and matching rows from orders table. So with only this join you will have 6 rows, each one with Johny Bravo as user, but with different orders data.
Then - another LEFT JOIN. This time with example table. Again - a number of returned rows is a multiplication of rows from orders table and matching rows from example table. So without GROUP BY and COUNT you will have eight rows of result.
Now, the GROUP BY query part. What does it do? It just groups rows with matching GROUP BY column(s). So it will group all rows with same users.id. There are eight of them.
Standard COUNT() will return a number of rows with not null value. As there were eight rows, both counts will return 8.
Now, as #GPW suggested, the solution is a COUNT(DISTINCT x). This function returns a count of unique not null rows.
Thus, the query should look like:
SELECT users.id, users.name, count(DISTINCT orders.id), count(DISTINCT example.id)
FROM users
LEFT JOIN orders ON orders.users_id=users.id
LEFT JOIN example ON example.number=orders.number
GROUP BY users.id
UPDATE - ordering and strict databases
You have also asked about ordering the result. You can order it by any column from your query. As MySQL is not very strict when it comes to grouping, you will also be able to order by any column from users table, as users table results are unique (grouped by id). You can also add, for example ORDER BY COUNT(DISTINCT orders.id) DESC to find users with largest number of orders.
Most databases, though, is more strict in GROUP BY queries. It allows to SELECT only columns with aggregated values or those explicitly contained in GROUP BY clause. So your GROUP BY clause should rather look like
GROUP BY users.id, users.name

fetch rows dynamically from one table and get values from other table sum it up and insert into target table

x table has columns(Agency,datac)Y table has columns(Agencyname,total)
EXAMPLE
Agency | datac
NET | 100
GOO | 300
NET | 100
GOO | 100
Agencyname | total
NET |
GOO |
first customer adds Agencyname then it gets update on table Y From a dropdown menu in html i have made user to either choose NET OR GOO but what i want is how many values they may enter in table X.
i want their total sum to be inserted into table Y. so that my expected output looks like this
Agencyname | total
NET | 200
GOO | 400
The most straight forward way is probably an update with a subquery;
UPDATE TableY
SET total = (SELECT SUM(datac) FROM TableX WHERE Agency=AgencyName)
An SQLfiddle to test with.
UPDATE Y
SET Y.total = X.Total
FROM TABLE_Y Y INNER JOIN
(
SELECT Agency, SUM(datac) AS Total
FROM TABLE_X
GROUP BY Agency
) X
ON Y.Agencyname = X.Agency
SQL FIDDLE TEST
SELECT agency, SUM(datac) FROM x GROUP BY agency
You want to combine this with INSERT INTO.
Although I do not see much sense in caching this easy-to-compute values...

how to optimize my query?

I have 3 tables country_data, user_data and topic_data with table structures as given.
country_data:
name | code
---------------|---------------
India | IN
United States | US
Australia | AU
user_data:
user_ip | topic_code | country
---------------|---------------|---------------
192.168.1.1 | topic_code_1 | India
192.168.1.2 | topic_code_2 | United States
192.168.1.3 | topic_code_3 | Australia
topic_data:
name | code
---------------|---------------
topic_1 | topic_code_1
topic_2 | topic_code_2
topic_3 | topic_code_3
I have about one hundred thousand(100,000) rows in user_data table.
What I want is, I need to filter the count of users from each country with its corresponding country code for a given topic. For example, I need the count of users who viewed topic_2 in each country. The requered output format is
country_code | count
---------------|---------------
IN | 150
US | 120
AU | 100
Now please check my query:
SELECT cd.code, COUNT(ud.country) as count
FROM topic_data as td, user_data as ud, country_data as cd
WHERE td.name = 'topic_1' AND td.code = ud.topic_code AND ud.country = cd.name
GROUP BY ud.country
This one takes about 2 seconds to complete the execution in phpmyadmin. In the php webpage, it takes 15 seconds to load the page even in the server. by removing the group by in the query, ie GROUP BY ud.country, it takes more than 30 seconds to execute and the output is with the last country code and total of all countries visits. what am I doing wrong? please help.
----UPDATE----
Altered the tables using foreign keys and so as my queries too. now it works with lightning speed. thanks for those who helped.
The query doesn't look too bad IMO. However the normalization of the data looks a bit strange, e.g. why would you have a country (name) field on user_data table, just to join into country on name to look up the code? Instead, the more logical thing to me would be to reference country by country code (or other indexed key constraint). This would also save a join to country, if you just need the code as per your example query. If user_data is a high volume table, you will want to keep the data in it to a minimum to reduce IO when reading (density).
Also, as an aside, joining using JOIN instead of in the WHERE clause will improve the readability of your code, IMO:
SELECT cd.code, COUNT(ud.country) as count
FROM topic_data as td
INNER JOIN user_data as ud
ON td.code = ud.topic_code
INNER JOIN country_data as cd
ON ud.country = cd.name
WHERE td.name = 'topic_1'
GROUP BY ud.country;
To address the performance issues, check that the following indexes are in place:
Index on topic_data.name
Index on the foreign keys user_data.topic_code and user_data.country (or user_data.country_code if you do change the foreign key to user_data.country_code)
try this instead:
use below database structure for using numerical matching in INNER JOIN statement may decrease search time,
so index your id column of tables (e.g. primary key):
**country_data**
id|name | code
--|---------------|---------------
1 |India | IN
2 |United States | US
3 |Australia | AU
**user_data**
user_ip | topic_id | county_id
---------------|-----------|---------------
192.168.1.1 | 1 | 1
192.168.1.2 | 2 | 2
192.168.1.3 | 3 | 3
**topic_data**
id|name
--|------------
1 |topic_1
2 |topic_2
3 |topic_3
and run multiple INNER JOIN statment like:
SELECT cd.code, count(ud.topic_code) as count
FROM ud
INNER JOIN cd ON cd.id = ud.country
INNER JOIN td ON td.id = ud.topic_code
WHERE td.code='topic_1'
GROUP BY ud.country;

Advance query. Rank most related fields in mysql

Let's assume we have a database like this:
Project_tbl:
-----------------
id | Project_name
-----------------
1 | A
2 | B
3 | C
-----------------
personel_project_tbl:
--------------------
user_id | Project_id
--------------------
1 | 1
2 | 2
3 | 1
3 | 2
2 | 3
--------------------
instrument_project_tbl:
--------------------------
instrument_id | Project_id
--------------------------
1 | 1
1 | 2
2 | 2
2 | 1
1 | 3
--------------------------
Now, I need to sort the list of projects and rank them with regard to their similarity to the project A.
For example:
A and B have 1 users in common over the 3 users and 2 instruments over the 2 instrument so their similarity ranking is (1/2 + 2/2) / 2 = 75%
A and C have no user in common but have 1 over 2 instruments so it will be (1/2)/2 = 25%
So B is more similar than be and output should be
--------------
Project | Rank
--------------
2 | 75
3 | 25
That's the first solution came to my mind...
If I did it in PHP and MySQL, it would be something like:
for all tables as table_x
for all projects (except A) as prj_y
unique = (Select distinct count(items) from table_x where project is A)
count += (Select distinct count(items) from table_x
where project is prj_x and items are in
(select distinct items from table_x where project is a)
)/unique
So the complexity would be O(n2) and with indexing the select also would cost O(log n) which wouldn't be affordable.
Do you have any idea to do it totally in MySQL or do it in a better and faster way?
******** More information and notes:**
I'm limited to PHP and MySQL.
This is just an example, in my real project the tables are more than 20 tables so the solution should have high performance.
this question is the supplementary question for this one : Get the most repeated similar fields in MySQL database if yr solution can be used or applied in a way for both of them (somehow) It would be more than great.
I want to multiply the value of related projects with the similarity of items to get the best option...
In conclusion, these two questions will : get the most related projects, get the similar items of all projects and find the most similar item for current project where the project is also similar to the current one! yo
Thanks for your intellectual answers, its really appreciated if you could shed some light on the situations
You could do it this way:
SET #Aid = (SELECT id
FROM Project_tbl
WHERE Project_name = 'A');
SELECT P.id
, (IFNULL(personel.prop, 0) +
IFNULL(instrument.prop, 0)
)/2*100 Rank
, personel.prop AS personell
, instrument.prop AS instrument
FROM Project_tbl P
LEFT JOIN
( SELECT B.Project_id pid, COUNT(*)/C.ref prop
FROM personel_project_tbl A,
personel_project_tbl B,
(SELECT COUNT(*) AS ref
FROM personel_project_tbl
WHERE Project_id = #Aid
) AS C
WHERE A.user_id = B.user_id
AND A.Project_id = #Aid
GROUP BY B.Project_id
) personel ON P.id = personel.pid
LEFT JOIN
( SELECT B.Project_id pid, COUNT(*)/C.ref prop
FROM instrument_project_tbl A,
instrument_project_tbl B,
(SELECT COUNT(*) AS ref
FROM instrument_project_tbl
WHERE Project_id = #Aid
) AS C
WHERE A.instrument_id = B.instrument_id
AND A.Project_id = #Aid
GROUP BY B.Project_id
) instrument ON P.id = instrument.pid
WHERE P.id <> #Aid
ORDER BY Rank DESC
The idea is to have one subquery for each table, and each of these subqueries maps project id to correspondence ratio for a given table.
I'm saying nothing at all about performance. You'll have to try and see whether it is fast enough for your needs, but as I see it there is no way to beat the O(n2) complexity you mention, as you have to inspect all the data.

PHP MySQL Select ID from one table and information from another table

I have two tables, one table is called queuelist and the other is call info. In the queuelist table it just lists different IDs. I am trying to get the 'clientID' from that table and match it with the 'ID' in the other table that contains all of the info and display it back on the page. Here is how the tables look:
Table - queuelist
ID | clientID
-------------
1 | 589
2 | 254
3 | 486
Table - info
ID | Name | Phone
--------------------
256 | Bob | 5551231234
486 | Jack | 5551231234
589 | Jill | 5551231234
This is what they call joining tables, you should use a query like this:
SELECT i.ID, i.Name, i.Phone FROM `queuelist` AS q
LEFT JOIN `info` AS i ON (
q.clientID = i.ID
);
I'm using aliases for shorter notation in the above query (queuelist becomes q and info becomes i) and then set the join condition (the bit between the ON()) to be the clientID from the queuelist table should match the ID in the info table.
Also see http://dev.mysql.com/doc/refman/5.0/en/join.html for more details.
You need to use an inner join
select * from queuelist as ql inner join info as i on ql.clientID = i.ID
Though you might want to replace * with specific field names e.g
select ql.clientID, i.fieldname FROM....
Well, I see no difficulty in this using a JOIN.
SELECT * FROM queuelist JOIN info ON clientID = info.ID WHERE queuelist.ID = 2
"Where" would be another option.
SELECT Name, Phone FROM queuelist,info WHERE clientID = ID
Assuming you want only name and phone

Categories