How do I write this query? - php

I have the following mysql table:
map_id - module_id - category_id
1 - 3 - 6
2 - 3 - 9
3 - 3 - 11
4 - 4 - 6
5 - 4 - 9
6 - 4 - 12
map_id is the primary key. I have a list of category_id in an array.
My select criteria is:
Both module_id = 3, 4 should be returned if category_id array contains 6,9.
Only module_id = 3 should be returned if category_id array contains 6,9,11
Right now if I run a simple WHERE...IN query then all the rows are selected, which is not what I want.
Edit: Actually I was looking for a more dynamic solution. Probably my question wasn't clear (it is my first one).
say I have these category_id's:
$cat = array(6,9)
If I query
"SELECT module_id FROM maptable WHERE category_id IN (".implode(',', $cat).")"
then I get rows with both module_id 3 and 4.
On the other hand, if the array is
$cat = array(6,9,11)
and I run the same query, I again get same result. But I want only rows with module_id = 3 this time.

SQL isn't really designed for a query like this, but it can be done.
SELECT module_id FROM
(SELECT module_id FROM table WHERE category_id = 6) cat_6
JOIN (SELECT module_id FROM table WHERE category_id = 9) cat_9 USING (module_id)
JOIN (SELECT module_id FROM table WHERE category_id = 11) cat_11 USING (module_id)
Make sure to have an index on category_id, module_id and a second index on module_id, category_id. Ideally you make those unique indexes.

WHERE ((category_id = 6 OR category_id=9) AND module_id=3) OR ((category_id = 6 OR category_id=9 OR category_id = 11) AND (module_id=3 OR module_id=4))
?
There's probably a simpler way of writing this, but I think you get the point :-)

when you use WHERE...IN it means 'any of in(...) value'
you can use AND instead

You could try:
SELECT * FROM `table`
WHERE (`module_id` IN(3,4) AND `category_id` IN(6,9))
OR (`module_id` IN(3) AND `category_id` IN(6,9,11))

Related

Selecting a distinct value and the sum of times it shows up in a column

I have an existing table with millions of entries (growing) that consists of:
userid|name|etc...
1 frank ...
1 frank ...
2 joe ...
5 sam ...
1 franky ...
What I need to do is return a table of:
place|name|total
1 franky 3
2 sam 1
3 joe 1
Where total is the SUM(userid = the distinct userid).
Currently I'm doing a query to SELECT DISTINCT userid from table and then foreach returned value in php, I'm doing another query to return the name and sum(userid = userid).
As you can assume, this is very taxing and takes a long time now with all of the values. Is there any way to speed this up by doing 1 query?
i think you need
SELECT #a:=#a+1 AS `place`, name, COUNT(userid) AS `total`
FROM `your_table`, (SELECT #a:= 0) AS a
GROUP BY userid
SELECT userid, COUNT(*)
FROM some_table
GROUP BY userid

Mysql select that matches all array elements [duplicate]

This question already has an answer here:
Select all rows that have at least a list of features
(1 answer)
Closed 9 years ago.
I have a mysql table
Table A
--------------------
item_id category_id
--------------------
1 1
1 2
1 4
2 1
2 3
Would like to make an sql query that will select all matches in an array
example:
given category_ids are 1,4 it should return only item_id 1
given category_ids are 1 it should return item_id 1 and 2
Thanks
For categories 1, 4:
SELECT item_id, COUNT(*) c
FROM TableA
WHERE category_id IN (1, 4)
GROUP BY item_id
HAVING c = 2
For category 1:
SELECT item_id, COUNT(*) c
FROM TableA
WHERE category_id IN (1)
GROUP BY item_id
HAVING c = 1
I think you should be able to see the pattern -- the HAVING clause should match the number of categories.
This assumes that item_id, category_id is unique in the table.
given category_ids are 1,4
SELECT item_id, category_id
FROM TableA
WHERE category_id IN (1, 4)
given category_ids are 1
SELECT item_id, category_id
FROM TableA
WHERE category_id IN (1)

MYSQL returning duplicate rows

I have a Database table in MYSQL, it looks like this:
Project_ID user_ID name
11 1 fred
11 2 rick
11 1 fred
I want to get the names in the table, but when I display it I get the same name twice because the user_ID 1 appears twice in the table. I tried to display it with GROUP BY and SUM, but I don't want to do it this way. How do I get it not to return duplicate rows?
Use DISTINCT
SELECT DISTINCT user_ID
, verschil_ma
FROM project_uren
WHERE project_ID = 11
GROUP BY user_ID
Point 1 is that should the user be assigned to the project twice?
Point 2 - Use the DISTINCT keyword to return only unique records - http://dev.mysql.com/doc/refman/5.0/en/distinct-optimization.html
SELECT DISTINCT user_ID
FROM TABLE
WHERE Project_id = 11
That will return you 1 and 2 (You won't get 1 twice)
Thanks
$results = // query
$results = array_unique($results);

Ordering by a prototype

There are 6 possible keys in a MySQL field. Lets call them types. Through PHP, I have defined an array, that is called $order, and arranges these types in order I want them to appear.
There is a table, articles, which has a field articles.type . Any article can have 0-6 types added to it. Now, what I want to do, is grab all of the articles, and order them from the prototype. What is the best way to do this? Can this be done in MySQL, since I suppose that would be faster? And if not, how can it be done in PHP?
Example:
Table:
id articleId type
1 3 type1
2 3 type2
3 3 type3
4 3 type4
5 4 type5
6 4 type6
7 5 type5
8 7 type1
9 7 type5
Order:
$order=array('type1','type2','type3','type4','type5','type6');
How do I fetch the results ordered by my $order variable?
You'd need to massage that array into a mysql-style if/case statement:
$order_by = "ORDER BY CASE";
$pos = 1;
foreach ($order as $clause) {
$order_by .= " CASE `type`='$clause' THEN " . $pos++;
}
$order_by .= " ELSE " . $pos++;
which would generate something like
ORDER BY CASE
WHEN `type`='type1' THEN 1
WHEN 'type`='type2' THEN 2
...
ELSE n
Can this be done in MySQL, since I suppose that would be faster?
Only if you allow MySQL to use an index
You can create a temp table:
$query = "CREATE TABLE IF NOT EXISTS test (
sort INT NOT NULL AUTO_INCREMENT,
`type` VARCHAR(20) NOT NULL,
PRIMARY KEY (sort),
KEY (`type`, sort)
ENGINE=MEMORY (SELECT 1,'other' <<-- see query below.
UNION SELECT 2,'type1' <<-- build this part using
UNION SELECT 3,'type2' <<-- Marc B's code.
UNION SELECT 4,'type3'
UNION SELECT 5,'type4'
UNION SELECT 6,'type5'
UNION SELECT 7,'type6' ";
Run this query.
Now you can link against this query using a join and use test.sort as your sortkey:
SELECT t1.id, t1.article_id, COALESCE(t.`type`,'other') as sort_type
FROM table1 t1
LEFT JOIN test t ON (t1.`type` = t.`type`)
WHERE ....
ORDER BY t.sort;
This query will be fully indexed and run as fast as possible.

where id = multiple artists

Any time there is an update within my music community (song comment, artist update, new song added, yadda yadda yadda), a new row is inserted in my "updates" table. The row houses the artist id involved along with other information (what type of change, time and date, etc).
My users have a "favorite artists" section where they can do just that -- mark artists as their favorites. As such, I'd like to create a new feature that shows the user the changes made to their various favorite artists.
How should I be doing this efficiently?
SELECT *
FROM table_updates
WHERE artist_id = 1
OR artist_id = 500
OR artist_id = 60032
Keep in mind, a user could have 43,000 of our artists marked as a favorite.
Thoughts?
This depends on how your database is setup. If I had my way, I'd set it up with a table like so:
Table: user_favourite_artist
user_id | artist_id
---------------------
1 | 2
1 | 8
1 | 13
2 | 2
3 | 6
6 | 20
6 | 1
6 | 3
user_id and artist_id together would be a composite primary key. Each row specifies a user, by id, and an artist they have as a favourite, by id. A query like so:
SELECT artist_id FROM user_favourite_artist WHERE user_id = 1
Would give you the artist_id's 2, 8, and 13. This is a very simple query that will scale to your expectations.
On the reverse, when an artist is updated, you'd run this query:
SELECT user_id FROM user_favourite_artist WHERE artist_id = 2
And you would get the user_id's 1 and 2. This will tell you which users to notify. This query is also simple and will scale.
Maybe you can try this:
SELECT *
FROM table_updates
WHERE artist_id IN(1, 500, 60032)
If you have the marked artists in a secondary table, I would recomend rather using a join.
Something like
SELECT *
FORM table_updates tu INNER JOIN
table_marked_by_user tmbu ON tu.artist_id = tmbu.artist_id
WHERE tmbu.user_id = $user_id
If you're on SQL Server, you can use a nested select statement:
select * from table_updates where artist_id in
(select artist_id from favorites_table where user_id = 10)
If you don't mind doing dirty reads, you can speed it up with (nolock).
select * from table_updates (nolock) where artist_id in
(select artist_id from favorites_table (nolock) where user_id = 10)

Categories