I have the following query:
select *
from user_interests
join interests using(interest_id)
where tag in('running', 'biking')
and user_id != 1;
Note: The in() values are built dynamically using php, so their could be one value or 100 values, each passed in via user browser $_GET.
My user_interests table is pretty simple, as it is just a list of the interest_id and the user_id.
CREATE TABLE `user_interests` (
`interest_id` INT(10) UNSIGNED NOT NULL,
`user_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`interest_id`, `user_id`)
);
My interests table is a simple table that holds a list of different types of interests (running, biking, etc.)
CREATE TABLE `interests` (
`interest_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`tag` VARCHAR(50) NOT NULL,
`category_id` TINYINT(3) UNSIGNED NOT NULL,
PRIMARY KEY (`interest_id`),
UNIQUE INDEX `tag` (`tag`)
);
Since each user can have many interests, how can I search the database for users that have all of the interests in the list. For the query above that would be running and biking. My query just gets people who have at least one of the above, how can I make it so it gets users who have all of the queried interests?
So if a user has 5 interests, and I pass in 2 and they have those 2 in their list of 5, their profile should be returned. If they only have 1 of the 2 passed in there profile should not be returned.
Here is a Fiddle: http://sqlfiddle.com/#!9/29ea2
I would try that
SELECT *
FROM user_interests
WHERE user_interests.user_id IN (
SELECT ui.user_id
FROM user_interests ui
left join interests i using(interest_id)
WHERE i.tag IN ($php_array)
^ PHP
and ui.user_id != 1
GROUP BY ui.user_id
HAVING COUNT(i.tag) >= count($php_array)
^ PHP
)
Fiddle
Related
I'm having following tables structure
CREATE TABLE IF NOT EXISTS `review_author` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`email` varchar(255) NOT NULL,
`client_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IDX_37D99F0819EB6921` (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2110 ;
AND
CREATE TABLE IF NOT EXISTS `brokers_comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`hb_broker_id` int(11) NOT NULL,
`client_id` int(11) DEFAULT NULL,
`user_name` varchar(100) NOT NULL,
`user_email` varchar(100) NOT NULL,
`state` int(11) NOT NULL DEFAULT '0',
`text` varchar(3000) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_5365DFFB9FE55EF7` (`hb_broker_id`),
KEY `IDX_5365DFFB19EB6921` (`client_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1583 ;
Before extracting value i did following query:
INSERT INTO review_author (
name,
email,
client_id
)
SELECT
brokers_comments.user_name,
brokers_comments.user_email,
brokers_comments.client_id
FROM brokers_comments
LEFT JOIN review_author
ON brokers_comments.user_name=review_author.name AND
brokers_comments.user_email=review_author.email AND
brokers_comments.client_id=review_author.client_id
WHERE review_author.id IS NULL
Not in review_author should be all author from table brokers_comments and now i'm trying to get authors id using following query:
SELECT
review_author.id
FROM brokers_comments
LEFT JOIN review_author
ON brokers_comments.user_name=review_author.name AND
brokers_comments.user_email=review_author.email AND
brokers_comments.client_id=review_author.client_id
WHERE review_author.id IS NOT NULL
but i'm getting about 110 results from total 1531 records from table brokers_comments.
UPDATE
I couldn't manage to insert data in http://sqlfiddle.com/ so following link are dump for two tables review_author and brokers_comments.
Again my issue is to transfer distinct columns(user_name, user_email, client_id) from table brokers_comments to table review_author and then select review_author.id based on relation name/email/client_id from both tables.
http://wrttn.in/7ca325
http://wrttn.in/3a7885
Insert new author was wrong and made duplication. Below is new correct form.
INSERT INTO review_author (
name,
email,
client_id
)
SELECT user_name, user_email, client_id
FROM brokers_comments AS broker
WHERE NOT EXISTS
(
SELECT 1
FROM review_author AS author
WHERE author.email = broker.user_email
)
GROUP BY broker.user_email
P.S. I somebody will make a working online mysql database please put in comments so i could put it there.
Resolved
Only now i realised that user_email must be unique. Based on this i made following select statement:
SELECT
author.id
FROM brokers_comments AS broker
LEFT JOIN review_author AS author
ON broker.user_email = author.email
It seems you use excess fields in JOIN clause since client_id is a key, you need to join tables only on this field. Possible cause of that you getting not same number of records is different name/email for same client_id in those two tables. So, your two queries should be like this:
INSERT INTO review_author (
name,
email,
client_id
)
SELECT
brokers_comments.user_name,
brokers_comments.user_email,
brokers_comments.client_id
FROM brokers_comments
LEFT JOIN review_author
ON brokers_comments.client_id=review_author.client_id
WHERE review_author.id IS NULL
and
SELECT
review_author.id
FROM brokers_comments
LEFT JOIN review_author
ON brokers_comments.client_id=review_author.client_id
WHERE review_author.id IS NOT NULL
I have four tables as follows:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `categories_friends` (
`category_id` int(10) unsigned NOT NULL,
`friend_id` int(10) unsigned NOT NULL,
UNIQUE KEY `category_id` (`friend_id`,`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `friends` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`friend_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`,`friend_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `ratings` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`category_id` int(10) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`rating` tinyint(2) unsigned NOT NULL,
`public` tinyint(1) NOT NULL DEFAULT '0',
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
I am trying to perform the following query on those tables:
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
INNER JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`category_id` = `Rating`.`category_id`
AND `JCategoriesFriend`.`friend_id` = `JFriend`.`id`)
INNER JOIN `favred`.`categories` AS `JCategory`
ON (`JCategory`.`id` = `Rating`.`category_id`
AND `JCategory`.`id` = `JCategoriesFriend`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `Rating`.`user_id` <> 1
AND `JCategory`.`id` IN (4, 14)
GROUP BY `Rating`.`id`
The query above is not working, as it returns no results (although there is data in the tables that should return), what I'm trying to do is to find all the Ratings that were not authored by me (ID:1), but were authored by my Friends, but only if I've selected to view a specific Category for that Friend, with the resulting set being filtered by a given set of specific Categories.
The INNER JOINs loop around through Rating --> Friend --> CategoriesFreind --> Category --> back to Rating.
If I remove the additional portion of the INNER JOIN's ON clauses as follows:
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
INNER JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`friend_id` = `JFriend`.`id`)
INNER JOIN `favred`.`categories` AS `JCategory`
ON (`JCategory`.`id` = `JCategoriesFriend`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `Rating`.`user_id` <> 1
AND `JCategory`.`id` IN (4, 14)
GROUP BY `Rating`.`id`
then the query will return results, but because the INNER JOIN joining the CategoriesFriend to the Rating is not being filtered by the 'JCategory'.'id' IN (4, 14) clause, it returns all Ratings by that friend instead of filtered as it should be.
Any suggestions on how to modify my query to get it to pull the filtered results?
And I'm using CakePHP, so a query that would fit into it's unique query format would be preferred although not required.
first ,why are you use the JFriend.id, does it mean something,or is it as the same as user_id?
try this one,the same logic but it's from top to bottom ,I feel:
SELECT * FROM categories as JCategory
INNER JOIN categories_friends as JCategoriesFriend ON JCategoriesFriend.category_id = JCategory.id
INNER JOIN friends AS JFriend ON JFriend.friend_id = JCategoriesFriend.friend_id
INNER JOIN ratings AS Rating ON Rating.user_id = JFriend.friend_id
WHERE JCategory.id IN (4,14) AND JFriend.user_id = 1 AND Rating.user_id <> 1 GROUP BY Rating.id
I got one result from all the data that I made for the testing.
if it does not work also,try make some correct data,maybe the data is not right...
the testing data below:
categories: id | name (14| 141414)
categories_friends: category_id| friend_id (14| 2)
friends: id | user_id | friend_id (4| 1| 2)
ratings: id | user_id | category_id | title (2| 2| 14 | 'haha')
So I wondered if the INNER JOINs were being a little too limiting and specific in their ON clauses. So I thought that maybe a LEFT JOIN would work better...
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
LEFT JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`friend_id` = `JFriend`.`id`
AND `JCategoriesFriend`.`category_id` = `Rating`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `JRatingsUser`.`id` IS NULL
AND `Rating`.`user_id` <> 1
GROUP BY `Rating`.`id`
That query worked for me.
I did away with linking to the categories table directly, and linked indirectly through the categories_friends table which sped up the query a little bit, and everything is working great.
I have redesigned my database structure to use PRIMARY and FOREIGN KEYs to link the entries in my 3 tables together, and I am having problems trying to write queries to select data in one table given data in a another table. Here is an example of my 3 CREATE TABLE statements:
CREATE TABLE IF NOT EXISTS players (
id INT(10) NOT NULL AUTO_INCREMENT,
username VARCHAR(16) NOT NULL,
uuid VARCHAR(200) NOT NULL DEFAULT 0,
joined TIMESTAMP DEFAULT 0,
last_seen TIMESTAMP DEFAULT 0,
PRIMARY KEY (id)
);
/* ^
One |
To
| One
v
*/
CREATE TABLE IF NOT EXISTS accounts (
id INT(10) NOT NULL AUTO_INCREMENT,
account_id INT(10) NOT NULL,
pass_hash VARCHAR(200) NOT NULL,
pass_salt VARCHAR(200) NOT NULL,
created BIGINT DEFAULT 0,
last_log_on BIGINT DEFAULT 0,
PRIMARY KEY (id),
FOREIGN KEY (account_id) REFERENCES players(id) ON DELETE CASCADE
) ENGINE=InnoDB;
/* ^
One |
To
| Many
v
*/
CREATE TABLE IF NOT EXISTS purchases (
id INT(10) NOT NULL AUTO_INCREMENT,
account_id INT(10) NOT NULL,
status VARCHAR(20) NOT NULL,
item INT NOT NULL,
price DOUBLE DEFAULT 0,
description VARCHAR(200) NOT NULL,
buyer_name VARCHAR(200) NOT NULL,
buyer_email VARCHAR(200) NOT NULL,
transaction_id VARCHAR(200) NOT NULL,
payment_type VARCHAR(20) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE
) ENGINE=InnoDB;
Say for example, I want to select all the usernames of users who purchased anything greater than $30. All the usernames are stored in the players table, which is linked to the accounts table and that is linked to the purchases table. Is this this the best way to design this relational database? If so, how would I run queries similar to the above example?
I was able to get get all of a users purchase history given their username, but I did it with 2 sub-queries... Getting that data should be easier than that!
Here is the SELECT query I ran to get all of a players purchase data:
SELECT *
FROM purchases
WHERE account_id = (SELECT id FROM accounts WHERE account_id = (SELECT id FROM players WHERE username = 'username'));
Also, when I try to make references to the other tables using something like 'players.username', I get an error saying that the column doesn't exist...
I appreciate any help! Thanks!
Your design is ok in my opinion. The relation between players and account is one-to-many and not one-to-one since this way, you can have two tuples referencing a single player.
I would write the query you need as:
SELECT DISTINCT p.id, p.username
FROM players p INNER JOIN accounts a ON (p.id = a.account_id)
INNER JOIN purchases pc ON (a.id = pc.account_id)
WHERE (pc.price > 30);
As Sam suggested, I added DISTINCT to avoid repeating id and username in case a user have multiple purchases.
Note the id is here to avoid confusion among repeated usernames.
Older questions seen
Counting one table of records for matching records of another table
MySQL Count matching records from multiple tables
Count records from two tables grouped by one field
Table(s) Schema
Table entries having data from 2005-01-25
CREATE TABLE `entries` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`ctg` VARCHAR(15) NOT NULL,
`msg` VARCHAR(200) NOT NULL,
`nick` VARCHAR(30) NOT NULL,
`date` DATETIME NOT NULL,
PRIMARY KEY (`id`),
INDEX `msg` (`msg`),
INDEX `date` (`date`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;
Child table magnets with regular data from 2011-11-08(There might be a few entries from before that)
CREATE TABLE `magnets` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`eid` INT(10) UNSIGNED NOT NULL,
`tth` CHAR(39) NOT NULL,
`size` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
`nick` VARCHAR(30) NOT NULL DEFAULT 'hjpotter92',
`date` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `eid_tth` (`eid`, `tth`),
INDEX `entriedID` (`eid`),
INDEX `tth_size` (`tth`, `size`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;
Question
I want to get the count of total number of entries by any particular nick(or user) entered in either of the table.
One of the entry in entries is populated at the same time as magnets and the subsequent entries of magnets can be from the same nick or different.
My Code
Try 1
SELECT `e`.id, COUNT(1), `e`.nick, `m`.nick
FROM `entries` `e`
INNER JOIN `magnets` `m`
ON `m`.`eid` = `e`.id
GROUP BY `e`.nick
Try 2
SELECT `e`.id, COUNT(1), `e`.nick
FROM `entries` `e`
GROUP BY `e`.nick
UNION ALL
SELECT `m`.eid, COUNT(1), `m`.nick
FROM `magnets` `m`
GROUP BY `m`.nick
The second try is generating some relevant outputs, but it contains double entries for all the nick which appear in both tables.
Also, I don't want to count twice, those entries/magnets which were inserted in the first query. Which is what the second UNION statement is doing. It takes in all the values from both tables.
SQL Fiddle link
Here is the link to a SQL Fiddle along with randomly populated entries.
I really hope someone can guide me through this. If it's any help, I will be using PHP for final display of data. So, my last resort would be to nest loops in PHP for the counting(which I am currently doing).
Desired output
The output that should be generated on the fiddle should be:
************************************************
** Nick ||| Count **
************************************************
** Nick1 ||| 10 **
** Nick2 ||| 9 **
** Nick3 ||| 6 **
** Nick4 ||| 10 **
************************************************
There might be a more efficient way but this works if I understand correctly:
SELECT SUM(cnt), nick FROM
(SELECT count(*) cnt, e.nick FROM entries e
LEFT JOIN magnets m ON (e.id=m.eid AND e.nick=m.nick)
WHERE eid IS NULL GROUP BY e.nick
UNION ALL
SELECT count(*) cnt, nick FROM magnets m GROUP BY nick) u
GROUP BY nick
I modeled a small database for easier explanation:
CREATE TABLE bands (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(120) NULL,
PRIMARY KEY(id)
)
TYPE=InnoDB;
CREATE TABLE albums (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
band_id INTEGER UNSIGNED NOT NULL,
album_name VARCHAR(120) NULL,
rating INTEGER UNSIGNED NULL,
insertion_date TIMESTAMP NULL,
PRIMARY KEY(id),
INDEX albums_FKIndex1(band_id),
FOREIGN KEY(band_id)
REFERENCES bands(id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
)
TYPE=InnoDB;
Now, pretending that we already have some bands and many albums registered in their respective tables, I want to select ONLY the last inserted album from each registered band.
PS: I have to use the "album.insertion_date" field to determine which album is the last inserted.
Try joining the two tables and filtering by insertion_date and band:
SELECT al.*
FROM albums al
INNER JOIN bands b ON al.band_id=b.id
WHERE al.insertion_date=(
SELECT max(insertion_date)
FROM albums
WHERE band_id=b.id
)
Try this one:
select b.name, a.album_name, a.isertion_date
from bands b, albums a
where a.band_id = b.id
and a.insertion_date = (select max(a1.insertion_date) from albums a1 where a1.band_id = b.id)
Considering that you have the albums' ids to be AUTO_INCREMENT and the possibility for the insertion_date to be NULL(as it is the default value), using insertion_date to determine the results is not the smartest thing to do but ... there you go:
SELECT DISTINCT band, last_album, insertion_date
FROM (
SELECT bands.name AS band, albums.album_name AS last_album, albums.insertion_date
FROM bands
JOIN albums ON bands.id=albums.band_id
ORDER BY albums.insertion_date DESC
) t1
GROUP BY band;