I've battling with this for a few days now and as SQL is not something I use too often I can't get my query to work the way I want it to.
I have a simple test database for tagging records using "toxi" solution (three tables, one acts as a link "ProgramCourses")
My tables
Program:
- ProgramID : int
- ProgramName : varchar
Course:
- CourseID : int
- CourseName : varchar
ProgamCourses:
- ProgramID : int
- CourseID : int
What I'm trying to do is to collect all tags as string and then add them as a new column to Program table.
My query
SELECT * , GROUP_CONCAT(c.CourseName) AS tags_list
FROM `Program` p
LEFT JOIN ProgramCourses AS pc ON p.ProgramID = pc.ProgramID
LEFT JOIN Course AS c ON pc.CourseID = c.CourseID
GROUP BY p.ProgramID
ORDER BY p.ProgramID
this kinda does what I need but it duplicates some columns and result I get is:
ProgramID ProgramName ProgramID CourseID CourseID CourseName tags_list
instead of
ProgramID ProgramName CourseName
Any help would be very appreciated.
If someone is kind enough to write a query for me could you please write it with full names of tables and columns as this would make it easier for me to understand and adopt for other purposes.
Thank you.
T.
DISTINCT c.CourseName Returns a count of the number of rows with different CourseName.
SELECT * , GROUP_CONCAT(DISTINCT c.CourseName) AS tags_list
FROM `Program` p
LEFT JOIN ProgramCourses AS pc ON p.ProgramID = pc.ProgramID
LEFT JOIN Course AS c ON pc.CourseID = c.CourseID
GROUP BY p.ProgramID
ORDER BY p.ProgramID
For More Info visit it Official
Try this
SELECT * , GROUP_CONCAT(c.CourseName) AS tags_list
FROM `Program` p
LEFT JOIN ProgramCourses AS pc using(ProgramID)
LEFT JOIN Course AS c using(CourseID)
GROUP BY p.ProgramID
ORDER BY p.ProgramID
Related
Every client has their own project but not all of them has project.
I want to select all clients that has project but I do not know how to do it!
I get all clients with this query:
SELECT `clients` FROM `reps` WHERE `clients` != ''
My goal is to get data which has only project
These are my database tables:
Table Clients: (Table name = reps)
1 id varchar(12) // example: stckvrflw
2 ctitle varchar(100) // example: StackOverflow
Table Projects: (Table name = verkocht)
1 id varchar(11) // example: 1
2 title varchar(100) // example: This is an Example
Do you have a solution on my problem?
You have the client in the milestones table, so doesn't this do what you want?
SELECT DISTINCT m.client
FROM `milestones` m;
You have to join the projects and the milestones tables. Since you gave no information on your database shema, the field names have to be adjusted. You also have to check for deleted or hidden flags if you have some:
SELECT c.* FROM clients c
INNER JOIN projects p ON p.client = c.id
INNER JOIN milestones m ON m.project = p.id
GROUP BY c.id
I have four schemas:
takes(ID,course_id,sec_id,semester,year)
student(ID,name,dept_name,tot_credit)
course(course_id,title,dept_name,credits)
department(dept_name,building,budget)
I want to create a query that finds the name and id of each Astronomy student whose name begins with the letter âTâ and who has not taken at least 16 Astronomy courses.
What's the easiest way I could do this?
I already wrote this beginning bit
SELECT name, id
FROM student
WHERE dept_name='Astronomy' AND name LIKE '%T%'
I'm not quite sure how to finish this off.
Any help would be greatly appreciated :)
Here's the result
NAME ID CLASS_TAKEN
-------------------- ----- -----------
Tolle 38279 12
Teo 62268 13
Tolle 93223 13
Tsukamoto 17707 5
Titi 11576 9
Teo 91772 12
Toraichi 50387 11
Tewari 80754 14
Tiroz 64091 14
9 rows selected
I need Teo with the id 91772 and Tewari 80754 to be gone
Given my reading of the requirements and the comments it's pretty clear that the question is not very clear. :-) What you're looking for are students where (total # of courses given by the Astronomy department) - (# of Astronomy courses taken by student) >= 16. So, how do we find these values? First, let's start with the total number of courses given by the Astronomy department. This is pretty simple:
SELECT COUNT(*) AS ASTRONOMY_COURSE_COUNT
FROM COURSE
WHERE DEPT_NAME = 'ASTRONOMY'
Now, the second part is to determine how many courses given by the Astronomy department each student has taken. To do this we need to start with the student, join to the courses the student has taken (the TAKES table), then join to the COURSES table to find out which department each course is part of. Something like the following should do it:
SELECT s.ID, s.NAME, COUNT(*) AS STUDENT_ASTRO_COUNT
FROM STUDENT s
INNER JOIN TAKES t
ON t.ID = s.ID
INNER JOIN COURSE c
ON c.COURSE_ID = t.COURSE_ID
WHERE c.DEPT_NAME = 'ASTRONOMY' AND
s.NAME LIKE 'T%'
GROUP BY s.ID, s.NAME;
OK, now we need to put this together. You've tagged this question for both Oracle and MySQL so I'm going to guess you'll accept valid syntax for either database; thus I'll use Oracle Common Table Expression syntax to pull everything together:
WITH ASTRONOMY_COURSES AS (SELECT COUNT(*) AS ASTRONOMY_COURSE_COUNT
FROM COURSE
WHERE DEPT_NAME = 'ASTRONOMY'),
STUDENT_ASTRO_COURSES AS (SELECT s.ID,
s.NAME,
COUNT(*) AS STUDENT_ASTRO_COUNT
FROM STUDENT s
INNER JOIN TAKES t
ON t.ID = s.ID
INNER JOIN COURSE c
ON c.COURSE_ID = t.COURSE_ID
WHERE c.DEPT_NAME = 'ASTRONOMY' AND
s.NAME LIKE 'T%'
GROUP BY ID)
SELECT s.ID,
s.NAME,
s.STUDENT_ASTRO_COUNT,
a.ASTRONOMY_COURSE_COUNT - s.STUDENT_ASTRO_COUNT AS UNTAKEN_COUNT
FROM STUDENT_ASTRO_COURSES s
CROSS JOIN ASTRONOMY_COURSES a
WHERE a.ASTRONOMY_COURSE_COUNT - s.STUDENT_ASTRO_COUNT >= 16;
Note here that a CROSS JOIN is used to put together the subqueries. This means that all the rows of each subquery are joined to all the rows of the other subquery - but since in this case the ASTRONOMY_COURSES subquery will only return a single row what we're doing is appending the ASTRONOMY_COURSE_COUNT value onto each row returned by the STUDENT_ASTRO_COURSES subquery.
That should at least get you pretty close. Amend as needed.
Not tested on animals - you'll be first! :-)
Share and enjoy.
Do you need to use all tables?
Table department has no links with the student,
Table takes has no links with the student,
Table coursehas no links with the student.
If student lists total credits that are all Astronomy I think this can be used:
select name, id, MAX(tot_credit) as credits
from student
where dept_name='Astronomy' and name like 'T%'
group by name, id
having MAX(tot_credit)<=16
PS - you schema is not good; PK-FK references are missing
Your query will need to reference more tables than just the student table.
Your tables seem be missing some important information, which student has taken which course. There's a table named takes, but there doesn't appear to be any relationship between takes and student.
So first, figure out how to list the students along with the Astronomy courses they have taken. Each row will identify the student and a course.
SELECT s.id AS student_id
, s.name AS student_name
, t.???
FROM student s
JOIN ??? t
ON t.student_id = s.id
WHERE ...
You may also need to include another "join" to an additional table, in order to identify which course a student has taken is an Astronomy course.
To also include students that have not take any Astronomy courses, you can use an outer join, rather than an inner join. (That would mean including the LEFT keyword before JOIN, and relocating predicates from the WHERE clause to the ON clause. (A predicate in the WHERE clause that can only be satisfied by non-NULL values will negate the outer-ness of the join.)
Once you have a query that returns that set (students along with any astronomy courses they've taken), you can then add a GROUP BY clause to "collapse" a set of rows into a single row. (Looks like you want the rows "grouped" by student.)
And then an aggregate function like COUNT() or SUM() can be used to get a count of rows for each group. (If you don't want to count any re-takes of a course (a "duplicate" course for a student) you may be able to make use of the COUNT(DISTINCT t.foo) form.
And then a HAVING clause can be added to the query, to compare the value returned from the aggregate expression to a constant value, to return only rows that satisfy a specific condition.
FOLLOWUP
Assuming:
CREATE TABLE course
( id INT UNSIGNED NOT NULL COMMENT 'PK'
, title VARCHAR(30) NOT NULL COMMENT 'course title'
, dept_name VARCHAR(30) NOT NULL COMMENT 'FK ref dept.name'
, credits DECIMAL(5,2) COMMENT 'credit hours'
, PRIMARY KEY (id)
);
CREATE TABLE student
( id INT UNSIGNED NOT NULL COMMENT 'PK'
, name VARCHAR(30) NOT NULL COMMENT 'student name'
, dept_name VARCHAR(30) NOT NULL COMMENT 'FK ref dept.name'
, tot_credit INT COMMENT '?'
, PRIMARY KEY (id)
);
CREATE TABLE takes
( student_id INT UNSIGNED NOT NULL COMMENT 'FK ref student.id'
, course_id INT UNSIGNED NOT NULL COMMENT 'FK ref course.id'
, sec_id INT UNSIGNED NOT NULL COMMENT '?'
, semester INT UNSIGNED NOT NULL COMMENT '?'
, year INT UNSIGNED NOT NULL COMMENT '?'
, PRIMARY KEY (student_id, course_id, sec_id, semester, year)
, CONSTRAINT FK_takes_course FOREIGN KEY (course_id) REFERENCES course (id)
, CONSTRAINT FK_takes_student FOREIGN KEY (student_id) REFERENCES student (id)
);
Query to get a list of students...
SELECT s.id
, s.name
FROM student s
WHERE s.name LIKE 'T%'
AND s.dept_name = 'ASTRONOMY'
Get list of students along with the courses they've taken, returning the id of the ASTRONOMY courses they've taken...
SELECT s.id AS student_id
, s.name AS student_name
, c.id AS course_id
FROM student s
LEFT
JOIN takes t
ON t.student_id = t.id
LEFT
JOIN course c
ON c.id = t.course_id
AND c.dept_name = 'ASTRONOMY'
WHERE s.name LIKE 'T%'
AND s.dept_name = 'ASTRONOMY'
Collapse the rows to one per student using a GROUP BY, and use aggregate functions to get counts or totals.
SELECT s.id AS student_id
, s.name AS student_name
, SUM(c.credits) AS total_astronomy_credits_taken
, COUNT(c.id) AS count_astronomy_courses_taken
, COUNT(DISTINCT c.id) AS count_distinct_astronomy_courses_taken
FROM student s
LEFT
JOIN takes t
ON t.student_id = t.id
LEFT
JOIN course c
ON c.id = t.course_id
AND c.dept_name = 'ASTRONOMY'
WHERE s.name LIKE 'T%'
AND s.dept_name = 'ASTRONOMY'
GROUP
BY s.id
, s.name
To omit rows from this resultset, add a HAVING clause. For example, to exclude rows where total_astronomy_credits_taken is greater than or equal to 12...
HAVING total_astronomy_credits_taken >= 12
If you want the rows returned in a certain sequence, specify that in an ORDER BY clause
ORDER BY s.id
If you want to replace NULL values from the aggregates with zeroes, you can warp the aggregate expression in an IFNULL(foo,0) function, e.g.
, IFNULL(COUNT(c.id),0) AS count_astronomy_courses_taken
Try this :
select a.name, a.id, count(b.ID) as class_taken
from student a inner join takes b
on a.ID = b.ID
inner join course c
on b.course_id = c.course_id
where a.dept_name='Astronomy' and substring(a.name,1,1) = 'T'
group by a.name, a.id
having count(b.ID) < 17
I am trying to optimise my php by doing as much work on the MySQL server as possible. I have this sql query which is pulling data out of a leads table, but at the same time joining two tags tables to combine the result. I am looking to add a company which is linked through a relations table.
So the table that holds the relationship between the two is relations_value which simply states (I add example data)
parenttable (companies) | parentrecordid (10) | childtable (leads) | childrecordid (1)
the companies table has quite a few columns but the only two relevant are;
id (10) | companyname (my company name)
So this query currently grabs everything I need but I want to bring the companyname into the query:
SELECT leads.id,
GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status,
leads.probability
FROM `gs_db_1002`.leads
LEFT JOIN ( SELECT *
FROM tags_module
WHERE tagid IN ( SELECT id
FROM tags
WHERE moduleid = 'leads' ) ) as b
ON leads.id = b.recordid
LEFT JOIN `gs_db_1002`.tags as c
ON b.tagid = c.id
GROUP BY leads.id,
leads.status,
leads.probability
I need to be able to go into the relations_values table and pull parenttable and parentrecordid by selecting childtable = leads and childrecordid = 1 and somehow join these so that I am able to get companyname as a column in the above query...
Is this possible?
I have created a sqlfiddle: sqlfiddle.com/#!2/023fa/2 So I am looking to add companies.companyname as column to the query.
I don't know what your primary keys and foreign keys are that link each table together.. if you could give a better understanding of what ID's are linked to eachother it would make this a lot easier... however i did something that does return the correct result... but since all of the ID's are = 1 then it could be incorrect.
SELECT
leads.id, GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status, leads.probability, companyname
FROM leads
LEFT JOIN (
SELECT * FROM tags_module WHERE tagid IN (
SELECT id FROM tags WHERE moduleid = 'leads' )
) as b ON leads.id = b.recordid
LEFT JOIN tags as c ON b.tagid = c.id
LEFT JOIN relations_values rv on rv.id = b.recordid
LEFT JOIN companies c1 on c1.createdby = rv.parentrecordid
GROUP BY leads.id,leads.status, leads.probability
I have various tables like
Student
primary id , students name, course
Papers
paper id, papername, course, semester, type
StudentOptions
primary id, studentid (foreign key - reference student id) and paperid (foreign key - references paper id)
StudentsTerm
studentid (foreign key- references student id) and student semester
Now the kind of result i want is,
I want to choose a course then the term, which will give me the number of papers/subject it has with their types (Mandatory/Optional) and with that i want to have the count of number of students studying those papers from all these tables.
I don't wanna create any view or stuff, Just a normal select query will do.
The query i am running is :
SELECT p_name,
p_id,
type,
Count(sps.studentid) AS counts
FROM students,
str,
papers
LEFT JOIN sps
ON sps.paperid = papers.p_id
WHERE sps.studentid = students.studentid
AND students.studentid = str.studentid
AND sps.studentid = str.studentid
AND str.semesterid = p_semid
AND str.sessionid = 12
AND students.course = c_id
AND c_id = 6
AND p_semid = 1
GROUP BY p_id
As better practice, if you are going to be using explicit JOIN syntax, then don't use implicit syntax. In the query you posted above, you select from papers, but you don't use it anywhere. Also, your column names are slightly ambiguous. If it's easier, alias each table using a single letter, or explicitly prefix the column names. If you're using an aggregate with GROUP BY, you cannot select columns which are not in the group.
Let's assume this is your ER diagram:
Let's first join all the tables:
SELECT a.id, a.name
FROM student a
JOIN str b ON b.student_id = a.id
JOIN sps c ON c.student_id = a.id
JOIN papers d ON d.id = c.paper_id
Now you wish to find the number of students studying papers from a specific course and type:
SELECT a.id, a.name
FROM student a
JOIN str b ON b.student_id = a.id
JOIN sps c ON c.student_id = a.id
JOIN papers d ON d.id = c.paper_id
WHERE b.semester = 12
AND d.course = 6
Because your attributes are ambigiuous, it is hard to tell what tables they are coming from. If you can set up the structure and sample data on SQL Fiddle, we could help you better.
I am working on a mysql query and its giving me headache!
The Scenario:
I am building a website where people can select industries they are interested in (NOTIFY_INDUSTRY). I join the selected values and store in a database field.
Example: a member selects agriculture (id = 9) and oil and gas (id = 13). I join them as 9-13 and store in the database.
Users can select several industries, not limited to two.
Also, members can select an industry (COMPANY_INDUSTRY) it belongs in assuming Information Technology which is stored in the database too.
Sample table (members):
ID
EMAIL
COMPANY_NAME
COMPANY_INDUSTRY
NOTIFY_INDUSTRY
The problem:
When a new user registers on the website, mail (the mails are sent on daily basis) is sent to existing users who have the new user's industry (COMPANY_INDUSTRY) as one of their interested industries (NOTIFY_INDUSTRY).
What i have done:
$sql="select id, email
from members
where notify_industry in (
select company_industry
from members
where datediff($today, date_activated) <= 1)"
This does not select the right members and i do not know the right way to go about it
EDIT - Exact Problem with current output:
Does not return any row, even when it should.
Assuming the new user's company_industry is 9, and there is an existing user with notify_industry: 10-9-20; it is meant to return the existing members email as the new member is in the existing member's categories of interest; but i get blanks
As #Shiplu pointed out, this is largely a normalization issue. Despite what some people seem to think, multi-value columns are murder to try to get right.
Your basic issue is:
You have members, who are interested in one or more companies/industries, which belong to one or more industries. You table structure should probably start as:
Industry
===============
id -- autoincrement
name -- varchar
Company
==============
id -- autoincrement
name -- varchar
Company_Industry
===============
companyId -- fk reference to Company.id
industryId -- fk reference to Industry.id
Member
===============
id -- autoincrement
name -- varchar
email -- varchar
Member_Interest_Industry
=========================
memberId -- fk reference to Member.id
industryId -- fk reference to Industry.id
Member_Interest_Company
========================
memberId -- fk reference to Member.id
companyId -- fk reference to Company.id
To get all companies a member is interested in (directly, or through an industry), you can then run something like this:
SELECT a.name, a.email, c.name
FROM Member as a
JOIN Member_Interest_Company as b
ON b.memberId = a.id
JOIN Company as c
ON c.id = b.companyId
WHERE a.id = :inputParm
UNION
SELECT a.name, a.email, d.name
FROM Member as a
JOIN Member_Interest_Industry as b
ON b.memberId = a.id
JOIN Company_Industry as c
ON c.industryId = b.industryId
JOIN Company as d
ON d.id = c.companyId
WHERE a.id = :inputParm
You should redesign the tables, as others have suggested.
However, barring that, there is a gross hack you can do:
SET sql_mode = 'ANSI';
SELECT notify_members.id, notify_members.email
FROM members notify_members
INNER JOIN members new_members
WHERE CURRENT_DATE - new_members.date_activated <= 1
AND
new_members.company_industry RLIKE ('[[:<:]](' || REPLACE(notify_members.notify_industry, '-', '|') || ')[[:>:]]');
Yuck. Basically, you turn 9-13 into the MySQL regular expression [[:<:]](9|13)[[:>:]], which matches 9, 13, 13-27-61, etc., but does not match 19-131 and the like. (This supports a compound COMPANY_INDUSTRY field, too.)
Use join SQL syntax rather than a select in style..
You need to join the members table to itself.
Currently:
select id, email
from members where notify_industry in
(select company_industry
from members
where datediff($today, date_activated) <= 1
)
Use this style:
select m1.id, m1.email
from members m1
inner join members m2 on m1.company_industry = m.notify_industry
where datediff($today, m2.date_activated) <= 1
Note the use of aliasing to m1 and m2 to help understand which id and emails are returned.
This may get a little ugly but you could try the following
WARNING This will make a Cartesian Product worthy of any Mad Scientist
SELECT NotifyIndustry.id,NotifyIndustry.email
FROM
(
SELECT CONCAT('-',COMPANY_INDUSTRY,'-') company FROM members
WHERE datediff($today, date_activated) <= 1)"
) CompanyIndustry
INNER JOIN
(
SELECT CONCAT('-', NOTIFY_INDUSTRY,'-') who_to_notify
FROM members
) NotifyIndustry
ON LOCATE(company,who_to_notify)>0;
probably not the fastest query ever but this should do the job:
select m_to_notify.id, m_to_notify.email
from members m_to_notify
join members m_new_member
on '-' || m_to_notify.notify_industry || '-'
like '%-' || m_new_member.company_industry || '-%'
where datediff($today, m_new_memberdate_activated) <= 1)