MySQL "For Each" - mysql

I'm trying to make a code question website, like CodeChef.
So far, I have two tables, problems and submissions.
problems consists of a single id field which is auto_incremented. (It really has more fields, but they are not relevant.)
submissions consists of an id field which is auto_incremented, a problem_id field which is foreign key-ed to problems.id, and a bool correct field.
Here are their (simplified) creation statements:
create table problems
(
id bigint auto_increment primary key not null,
/* more fields that are unimportant */
);
create table submissions
(
id bigint auto_increment primary key not null,
problem_id bigint not null,
correct bool not null,
/* more fields */
foreign key (problem_id) references problems(id)
);
Now, what I'm trying to do is the following:
For each id in problems, return a row consisting of that ID, the number of correct submissions for that problem, and the number of total submissions for that problem.
Basically, I want to use the following query:
select
problems.id,
sum(submissions.correct),
count(submissions.id)
from submissions inner join problems
on submissions.problem_id=problems.id and problems.id=[problem_id];
where [problem_id] goes over every id in problems.
It seems like I need to use a sub-query, but I tried it and it did not work at all.
How do I create a query to return these results?

Your query has some defaults.
You do not specify the grouping criteria for the aggregates you use
you join, this is a 'for each' per se. You don't need anything else than the join condition you already specified
Your join should be a left join since maybe there's no correct submission for your problem, leading to 0 results at all in case of an inner join.
This is my proposition:
SELECT
p.id,
SUM(IF(s.correct, 1, 0)) AS correct_submissions,
COUNT(s.id) AS total_submissions
FROM problems p
LEFT JOIN submissions s ON p.id = s.problem_id
GROUP BY
p.id

All you need for your query is a group by clause:
select problems.id,
sum(submissions.correct),
count(submissions.id)
from submissions inner join
problems
on submissions.problem_id=problems.id
group by problems.id;

try
select
problems.id,
sum(submissions.correct),
count(submissions.id)
from submissions inner join problems
on submissions.problem_id=problems.id
where problems.id=[problem_id]
group by problems.id;

Related

MySQL select rows from parent table only if records in child table match records in another child table

I have two sets of parent/child tables:
referrals / modalities_match_referrals
locations / modalities_match_providers
There is another table modalities which is a list of all of the modalities that we typically deal with.
For any referral, I need to find a list of providers (locations table) which can perform each of the modalities required by the referral. For example, if the referral requires an MRI and an arthrogram, the list of should be limited to those who can perform both an MRI and an arthrogram, even if such a provider offers other modalities besides the ones needed for this referral.
My question: is there a single query (with subqueries) that will result in a list of only those locations/providers who can perform all of the modalities linked to a referral through the modalities_match_referrals table?
My problem is one of over-inclusion when a referral requires more than one modality. My query is resulting in a list of providers than can perform at least one of the modalities and is including providers that are not able to perform all modalities.
Here are the child/matching tables:
CREATE TABLE modalities_match_referrals (
id INT AUTO_INCREMENT PRIMARY KEY NOT NULL
,referral_id INT NOT NULL
,modality_id INT NOT NULL
,on_off TINYINT DEFAULT 0
,INDEX(referral_id)
,INDEX(modality_id)
,FOREIGN KEY(referral_id) REFERENCES referrals(id)
,FOREIGN KEY(modality_id) REFERENCES modalities(id)
);
CREATE TABLE modalities_match_providers (
id INT AUTO_INCREMENT PRIMARY KEY NOT NULL
,location_id INT NOT NULL
,modality_id INT NOT NULL
,on_off TINYINT DEFAULT 0
,INDEX(location_id)
,INDEX(modality_id)
,FOREIGN KEY(location_id) REFERENCES locations(id)
,FOREIGN KEY(modality_id) REFERENCES modalities(id)
);
What I have tried so far
I have a long production query which is too long to include here, but I am getting the same list of providers from this shorter query and subqueries:
SELECT locations.id AS 'Location ID'
,locations.name AS 'Provider'
FROM locations
JOIN (
SELECT p.location_id
FROM modalities_match_providers AS p
RIGHT JOIN (
SELECT modality_id
FROM modalities_match_referrals
WHERE on_off=1
AND referral_id=9660
) AS r ON r.modality_id = p.modality_id
WHERE on_off=1
) AS l ON l.location_id=locations.id
WHERE locations.recycle=0
AND locations.locationstateid=1
GROUP BY locations.id
ORDER BY locations.name
LIMIT 10;
The results include providers that can perform MRIs and/or arthrograms, and what I am looking for are only those providers who can perform both.
In the past, the modalities where handled as columns in both the referrals and locations tables. The sql was a lot simpler but the issue with that system was having columns to those tables when a new modality was introduced (this happens more than you might think - open MRI vs stand up MRI, etc.)
I'm also thinking of creating a PHP class to be called during the while loop that renders the list onto the webpage. The class would do the comparing between modalities_match_referrals and modalities_match_providers and return true or false. Based on the return value, the provider would be displayed or not displayed. The downside there is complexity and I would be concerned about the performance of the page.
Thank you in advance for any help you can offer.
Cross join the referral modalities with the locations so as to get the optimal result. Then outer join the providers' modalities to see how much is actually covered. Use GROUP BY and HAVINGthe get the referral / location pairs that lack no modality.
select
r.referral_id,
l.id as location_id
from modalities_match_referrals r
cross join locations l
left outer join modalities_match_providers p
on p.referral_id = r.referral_id
and p.modality_id = r.modality_id
and p.location_id = l.id
group by r.referral_id, l.id
having count(*) = count(p.id)
order by r.referral_id, l.id;
This should give you all referrals with all providers that can perform all their modalities.

get all rows from table that have both variables in a same column

I need to get all company ids from a table that have both city ids (let's say 7333 and 10906) but id doesn't work the way I do it.
This is my table:
and this is my code
SELECT `company_id` as id
FROM `logistics_companies_destinations`
WHERE `city_id`= 7333 and `city_id` = 10906
MySQL does not have an INTERSECT keyword, but one way to implement this is:
SELECT `company_id` as id
FROM `logistics_companies_destinations`
WHERE
`city_id` = 10906
and `company_id` IN (SELECT `company_id` as id
FROM `logistics_companies_destinations`
WHERE `city_id`= 7333)
[Another way (as note by another poster) is to join to the table twice and apply the filter conditions one to each join].
You can also solve this using an INNER JOIN to the table itself, joining on the same company_id and requiring both city_ids to be present:
SELECT
`lcd1`.`company_id` AS id
FROM `logistics_companies_destinations` AS lcd1
INNER JOIN `logistics_companies_destinations` AS lcd2
WHERE `lcd1`.`city_id`= 7333 AND `lcd2`.`city_id` = 10906
Sorry, I usually also frown upon stupidly abbreviated table names like this, but did not come up with better aliases ;)
I believe your problem is the "and". You are looking for the company_id if the city_id equals "7333" AND "10906". Use an or and it should work.

Query with sub-results from another table

I have the following two database tables:
`membership_members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` tinytext NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
)
`membership_payments` (
`date` date NOT NULL,
`amount` int(11) NOT NULL,
`member` int(11) NOT NULL,
KEY `member` (`member`)
)
I wish to list all the members, and for each of them I would like to show the list of all the payments by that member. The membership_payments.member column is a foreign key membership_members.id (this is MySQL so I cannot explicitly specify a foreign key). Notice that I do want to list a member even if he doesn't have any payments.
So something like:
* John Smith
- 2012-05-06 $100
- 2012-01-02 $100
* Brian Jones
* Mike Jackson
- 2012-09-02 $50
I have tried with this query:
SELECT id, name, active, date, amount
FROM `membership_members`,
`membership_payments`
WHERE membership_members.id = member
This of course gives me tha data I need, but not exactly how I need it as it returns a row for each payment. That means that I later have to group the rows (in PHP) by member id so that I don't list a member multiple times. I can do that, but I believe that it would be easier and faster to do it in SQL. Also, this query only gives me users which have payments with their id.
I feel that this should be simple, but last time I did anything but the most simple stuff in SQL was 6-7 years ago.
EDIT:
LEFT OUTER JOIN suggested in one of the answers solves the issue with the "missing" members. But what is the best way of grouping results by member IDs? I know there is a GROUP BY clause, but it doesn't give me all the payments for the given member, only one. I suppose I can run a new payments query for each member, but I fear this would be very inefficient (we have around 300-400 members).
You'll need to JOIN the two tables based on referencing key columns.
SELECT id, name, active, date, amount
FROM membership_members
LEFT OUTER JOIN membership_payments
ON membership_members.id = membership_payments.member;
I chose LEFT OUTER JOIN so that the members without payments are also shown.
For more info on joins check http://dev.mysql.com/doc/refman/5.0/en/join.html
EDIT
Use ORDER BY membership_members.id to get records ordered by a certain column.
Grouping does not behave like sorting. GROUPING merges all records by the column you provided. ORDER BY sorts
use LEFT JOIN
basically, what you are doing now is an INNER JOIN. INNER JOIN only displays a records if it has atleast one record on each table. But LEFT JOIN operates differently. It displays all records on the Lefthand side table whether it has matching record or not on the righthand side table.
SELECT id, name, active, date, amount
FROM `membership_members` a
LEFT JOIN `membership_payments` b
ON a.ID = b.member
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Take a look at GROUP_CONCAT MySQL clause.
Example
OR
You can use CONCAT.
Example
SELECT CONCAT(`membership_payments.date`, ' ', `membership_payments.amount`)
FROM `membership_members`
LEFT OUTER JOIN membership_payments
ON membership_members.id = membership_payments.member;

Best way to get article from a database along with connected tags

I want to get articles and all tag names which they're connected to from a MySQL database. My SQL code:
CREATE TABLE articles (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
)
CREATE TABLE tags (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(25) NOT NULL,
PRIMARY KEY (id)
)
CREATE TABLE connections (
art_id INT UNSIGNED NOT NULL,
tag_id INT UNSIGNED NOT NULL
);
Should I perform several queries or make single big one? How can I achive that? I tried to play with JOIN, but it seems it cannot be done that way.
PS. I've been searching, but there is no EXACT question on SO.
select a.id, group_concat(t.name) as tags
from articles a
left join connections c on c.art_id = a.id
left join tag t on t.id = c.tag_id
group by a.id
This query returns 1 row per article, and group_concat() will return the names of all connected tags as a comma delimited list (or null if there are no connected tags).
You're off to a great start - but there are a few things that can be optimized here.
Let's also assume your Article table has an ID and a Title field (just to add some spice to this query).
To simply answer your questions:
Select a.ID "ID", a."Title" "Title", t.Name "Tag_Name"
From connections c
Left Join article a
on a.id = c.art_id
Left Join tags t
on t.id = c.tag_id
-- If you want to filter on a certain article ID
WHERE a.id = #SomeArticleParam
I would really advise a couple things to optimize these tables:
Since connections directly links articles and tags, I would add foreign key references to the connections table. Here is a good link: foreign key link.
Index your connections table - this will allow faster queries when referencing specific tags or articles. A Unique index will help prevent adding duplicate article/tag pairs in the connections table. Here is a good link to get you started: index link.
try this
select a.(*),t.name from articles a
inner join connection c on a.id=c.art_id
inner join tags t on c.tag_id=t.id
SELECT articles.*,tags.* FROM articles , tags,connections
where tags.id=connections.tag_id and articles.id=connections.art_id

sql on mysql about join

the code below provide a result too much Infact i want to list the customer that never buy somethink How can i fix the code below
SELECT
webboard.listweb.id,
webboard.listweb.iditempro,
webboard.listweb.url,
webboard.listweb.useradddate,
webboard.listweb.expiredate,
webboard.prorecord.urlpostonweb
webboard.prorecord.urlpostonweb
FROM
webboard.listweb ,
webboard.prorecord
Where listweb.id Not In
(select webboard.prorecord.idlist From webboard.prorecord )
Using the syntax
FROM
webboard.listweb ,
webboard.prorecord
will perform a cartesian, or cross, join on the tables involved. So for every row in the table listweb all the rows in prorecord are displayed.
You need to use an INNER JOIN to only select the rows in listweb that have related rows in the prorecord table. What are the fields which identify the rows (your Primary Keys) and what is the name of the foreign key field in the prorecord table?
EDIT: Just re-read the question and comments and I see you want the rows in listweb which do not have an entry in prorecord
Your SELECT will then look like:
SELECT
webboard.listweb.id,
webboard.listweb.iditempro,
webboard.listweb.url,
webboard.listweb.useradddate,
webboard.listweb.expiredate,
webboard.prorecord.urlpostonweb
-- webboard.prorecord.urlpostonweb -- You have this field twice
FROM webboard.listweb LEFT JOIN webboard.prorecord
ON webboard.listweb.id = webboard.prorecord.idlist -- I'm guessing at the foreign key here
WHERE webboard.prorecord.idlist IS NULL