SQL result missing a piece - mysql

I'll get straight to the point.
I've 2 tabels:
1) poll_options
| (ID) | Name |
| 1 | 0-5 |
| 2 | 5-10 |
| 3 | 10-15 |
| 4 | 15-20 |
| 5 | 20-25 |
| 6 | 25-30 |
| 7 | 30-35 |
| 8 | 35-40 |
| 9 | 40-45 |
| 10 | 45-50 |
| 11 | 50+ |
2) poll_votes
| ID | (poll_options_id) | vote_count | woning_id |
| 1 | 3 | 1 | 20 |
| 2 | 11 | 1 | 18 |
| 3 | 5 | 1 | 25 |
| 4 | 5 | 1 | 27 |
| 5 | 3 | 1 | 25 |
| 6 | 4 | 1 | 26 |
Linking them together is the ID from poll_options with poll_options_ID from poll_votes. I placed ( ) around them to make is visual.
I've made a query to show the total votes made for each answer.
SELECT o.name as answer, sum(v.vote_count) as total_votes_per_answer
FROM poll_options as o LEFT JOIN poll_votes as v ON v.poll_option_id = o.id
WHERE WONING_id='$woning_id'
GROUP BY o.name
ORDER BY o.id ASC
Here is where my problem is.
It only shows the name from poll_options that have a vote.
I would like to show all the name of poll_options and the ones where there is no vote yet, make them show 0.
for example (with current sql and woning_id=20):
| answer |total_votes_per_answer|
| 0-5 | 1 |
| 5-10 | 1 |
What I want it to be:
| answer |total_votes_per_answer|
| 0-5 | 1 |
| 5-10 | 1 |
| 10-15 | 0 |
| 15-20 | 0 |
| 20-25 | 0 |
| 25-30 | 0 |
| 30-35 | 0 |
| 35-40 | 0 |
| 40-45 | 0 |
| 45-50 | 0 |
| 50+ | 0 |
Is there something in SQL that I can use to get this result?

Your SQL is invalid, but it would work if you inserted and:
SELECT o.name as answer, sum(v.vote_count) as total_votes_per_answer
FROM poll_options o LEFT JOIN
poll_votes v
ON v.poll_option_id = o.id AND WONING_id = '$woning_id'
GROUP BY o.name
ORDER BY o.id ASC;
That said, you should be passing $woning_id as a parameter into the query. Don't munge query strings with such values -- they can cause unexpected and hard to find errors.

Related

How do I join 2 tables to a third one which contains the primary key?

I have 4 tables: the first is the client table, which has customer info, and client_id as an auto-increment primary key.
The second and third are identical in structure: they are used to track attendance to 2 different therapy programs. They each have a primary key, and a client_id column to track the client. One of the fields contains units, which I want to sum.
The last table contains the therapists' info.
Basically I want to extract total amount of units for each client from the two attendance tables.
I have tried LEFT JOINS to no avail. I also tried a UNION ALL, but couldn't get it to sum the units.
This is how the tables look:
client:
+---------------------------------------+
| client_id | f_name | l_name | th_id |
|-----------|----------|--------|-------|
| 1 | sherlock | holmes | 1 |
| 2 | john | watson | 4 |
| 3 | hercule | poirot | 3 |
| 4 | jane | marple | 2 |
+---------------------------------------+
therapist:
+--------------------------+
| th_id | f_name | l_name |
|-------|---------|--------|
| 1 | james | kirk |
| 2 | mr | spock |
| 3 | bones | mccoy |
| 4 | nyota | uhura |
+--------------------------+
attendance it:
+-------------------------------+
| it_id | client_id | units |
|-----------|-----------|-------|
| 1 | 1 | 4 |
| 2 | 1 | 4 |
| 3 | 1 | 0 |
| 4 | 1 | 2 |
| 5 | 4 | 0 |
| 6 | 4 | 4 |
| 7 | 4 | 0 |
| 8 | 4 | 2 |
+-------------------------------+
attendance psr:
+-------------------------------+
| it_id | client_id | units |
|-----------|-----------|-------|
| 1 | 1 | 16 |
| 2 | 1 | 16 |
| 3 | 1 | 0 |
| 4 | 1 | 12 |
| 5 | 4 | 0 |
| 6 | 4 | 14 |
| 7 | 4 | 8 |
| 8 | 4 | 10 |
+-------------------------------+
The result should look like this:
+------------------------------------------------------------+
| client_id | total_units_it | total_units_psr | therapist |
|-----------|----------------|-----------------|-------------|
| 1 | 10 | 44 | james kirk |
| 4 | 6 | 32 | mr spock |
+------------------------------------------------------------+
Please excuse the primitive representations, and please don't ask why the tables are designed like that... ;-) Also, I obviously ignored many other fields which are not relevant to the question, such as dates, etc.
Any advice would be appreciated.
Thanks!
You can't use join or you will create Cartesian product and duplicate the rows.
Instead you do a subquery:
SELECT c.*
, (SELECT SUM(units) FROM attendance_it a WHERE a.client_id = c.client_id ) as total_units_it
, (SELECT SUM(units) FROM attendance psr a WHERE a.client_id = c.client_id ) as total_units_psr
, t.*
FROM client c
JOIN therapist t
ON c.th_id = t.th_id
use group by client_id to get the sum of each client. and no need to use join as you have already the ids in column.

How to get count of combinations from database?

How to get count of combinations from database?
I have to database tables and want to get the count of combinations. Does anybody know how to put this in a database query, therefore I haven't a db request for each trip?
Trips
| ID | Driver | Date |
|----|--------|------------|
| 1 | A | 2015-12-15 |
| 2 | A | 2015-12-16 |
| 3 | B | 2015-12-17 |
| 4 | A | 2015-12-18 |
| 5 | A | 2015-12-19 |
Passengers
| ID | PassengerID | TripID |
|----|-------------|--------|
| 1 | B | 1 |
| 2 | C | 1 |
| 3 | D | 1 |
| 4 | B | 2 |
| 5 | D | 2 |
| 6 | A | 3 |
| 7 | B | 4 |
| 8 | D | 4 |
| 9 | B | 5 |
| 10 | C | 5 |
Expected result
| Driver | B-C-D | B-D | A | B-C |
|--------|-------|-----|---|-----|
| A | 1 | 2 | - | 1 |
| B | - | - | 1 | - |
Alternative
| Driver | Passengers | Count |
|--------|------------|-------|
| A | B-C-D | 1 |
| A | B-D | 2 |
| A | B-C | 1 |
| B | A | 1 |
Has anybody an idea?
Thanks a lot!
Try this:
SELECT Driver, Passengers, COUNT(*) AS `Count`
FROM (
SELECT t.ID, t.Driver,
GROUP_CONCAT(p.PassengerID
ORDER BY p.PassengerID
SEPARATOR '-') AS Passengers
FROM Trips AS t
INNER JOIN Passengers AS p ON t.ID = p.TripID
GROUP BY t.ID, t.Driver) AS t
GROUP BY Driver, Passengers
The above query will produce the alternative result set. The other result set can only be achieved using dynamic sql.
Demo here

MySQL, ordering GROUP BY

I have a table that has some values in it, along with the time that value was taken against an associated ID from another table.
I am looking to retrieve the latest value for every item in that table, and then order by those latest values.
Here is an SQL fiddle, http://www.sqlfiddle.com/#!2/0be99
And here is text output.
'hist' table
| HIST_ID | HIST_ITEM_ID | HIST_VALUE | HIST_TIME |
|---------|--------------|------------|------------|
| 1 | 1 | 1 | 1420291000 |
| 2 | 1 | 2 | 1420292000 |
| 3 | 1 | 3 | 1420293000 |
| 4 | 1 | 5 | 1420294000 |
| 5 | 1 | 10 | 1420295000 |
| 6 | 1 | 50 | 1420296000 |
| 7 | 1 | 60 | 1420297000 |
| 8 | 1 | 77 | 1420298000 |
| 9 | 1 | 90 | 1420299000 |
| 10 | 1 | 101 | 1420300000 |
| 11 | 2 | 1 | 1420291000 |
| 12 | 2 | 3 | 1420292000 |
| 13 | 2 | 7 | 1420293000 |
| 14 | 2 | 9 | 1420294000 |
| 15 | 2 | 15 | 1420295000 |
| 16 | 2 | 21 | 1420296000 |
| 17 | 2 | 33 | 1420297000 |
| 18 | 2 | 35 | 1420298000 |
| 19 | 2 | 55 | 1420299000 |
| 20 | 2 | 91 | 1420300000 |
'items' table
| ITEM_ID | ITEM_TITLE |
|---------|------------|
| 1 | ABCD |
| 2 | XYZ123 |
So, I can do something like...
select * from hist
inner join items on hist_item_id = item_id
group by hist_item_id
order by hist_value desc
However this returns me a grouping that I cannot order. How can I order this grouping? I had a look at other similar questions on here but was unable to apply their solutions successfully to my query to produce the desire result.
The desired result here would be to return.
HIST_ITEM_ID | ITEM_TITLE | HIST_VALUE |
|------------|------------|------------|
| 1 | ABCD | 101 |
| 2 | XYZ123 | 91 |
You can use a join to get the most recent history item. Then you can join back to the history table and the item table to get additional information:
select h.*, i.item_title
from (select hist_item_id, max(hist_id) as max_hist_id
from hist
group by hist_item_id
) hh join
hist h
on h.hist_id = hh.max_hist_id join
items i
on i.item_id = hh.hist_item_id;
Here is a SQL Fiddle.
You should use MAX function and group by the item id. That would look like this:
SELECT i.item_id, i.item_title, MAX(h.hist_value)
FROM items AS i
INNER JOIN hist AS h
ON i.item_id = h.hist_item_id
GROUP BY i.item_id

Creating a formulaic query with multiple columns and joined columns

I'm trying to produce a formula which pits our students' reward points against their negative behaviour flags.
Students are given LEAP points (in the transactions table) for their positive behaviour. They get more points depending on the category of their reward, i.e. Model Citizen gives the student 10 points.
On the other hand, students are given single Flags for negative behaviour. The category of the Flag is then weighted in a database table, i.e. the Aggressive Defiance category will have a high weighting of 4 whereas Low Level Disruption will only be worth 1.
The difficulty therefore is trying to factor in the Flag categories' weightings. They're stored in the categories table under the Weight column.
Here's the SQL fiddle: http://sqlfiddle.com/#!2/2e5756
In my head, the pseudo-SQL code would look something like this...
SELECT
CONCAT( stu.Surname, ", ", stu.Firstname ) AS `Student`,
SUM(t.Points) AS `LEAP Points`,
SUM(<<formula>>) AS `Flags`
( `LEAP Points` - `Flags` ) AS `Worked Out Points Thing`
FROM student stu
LEFT JOIN transactions t ON t.Recipient_ID = stu.id
LEFT JOIN flags f ON f.Student_ID = stu.id
LEFT JOIN categories c ON f.Category_ID = c.ID
GROUP BY stu.id
However, it's the <<formula>> that I have no idea how to implement in MySQL. It needs to be something like this:
SUM OF[ Each of Student's Flags * that Flag's Category Weighting ]
So, if a student has these flags...
#1 f.Reason "Being naughty", f.Category_ID "1", c.Title "Low Level Disruption", c.Weight "1"
#1 Reason "Aggressively naughty!", Category "Aggressive Defiance", Category Weighting "4"
#1 Reason "Missed detention", Category "Missed Detention", Category Weighting "3"
They would have a total of 1+4+3 = 9 points to use in the Worked Out Points Thing equation.
The desired output therefore is essentially...
Student LEAP Points Flags Equation Points LEAP Points minus Flag Points
D Wraight 1000 800 200
D Wraight2 500 800 -300
D Wraight3 1200 300 900
From the SQL fiddle above, here is the required output.. I've missed out some students because I had to work these out manually:
STUDENT FLAGS LEAP EQUATION
137608 4 (2+2) 12 (2+5+5) 8 (12-4)
139027 2 (2) 7 (2+5) 5 (7-2)
139041 4 (2+1+1+NULL) 8 (2+2+2+2) 4 (8-4)
139892 4 (4) 0 -4 (0-4)
138832 4 (4) 0 -4 (0-4)
34533 4 (4) 0 -4 (0-4)
137434 0 10 (2*5) 10 (10-0)
Which will help us to work out the choices we make available to each student when looking at end of year reward trips.
Hope that makes sense.. it's kinda boggled my head trying to explain it..
Thanks in advance,
figure out your 'formula' bit first because it's the deepest part. work outwards.
build a table of flags * weight per student
select sum(weight), student_id from flags f
join categories c
on f.category_id = c.id
group by student_id
so now you've got a table of flag values to minus from sum of transactions per student
select sum(points), recipient_id from transactions
group by recipient_id
so now we have two tables with positive and negative values by student id (assuming obviously that student id is recipient id)
you want those with transactions but without flags to appear in the result, so outer join.
and number minus null is null so ifnull function on the flags to get 0
select a.student, points - ifnull(penalties, 0) as netPoints
from
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
left outer join
(select sum(weight) as penalties, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
a.student = b.student
so with the name in there it's just
select
concat(firstname, ', ', surname) as name,
ifnull(points,0) as totalPoints,
ifnull(penalties,0) as totalPenalties,
ifnull(points,0) - ifnull(penalties, 0) as netPoints,
ifnull(countFlags, 0)
from
student
left join
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
on student.id = a.student
left join
(select sum(weight) as penalties, count(f.id) as countFlags, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
student.id = b.student
join condition is always from student's id column, which is never null.
there are probably more efficient ways, but who cares?
Returning to the question (and at the risk of repeating myself!), given the following data set, what would the desired result set look like...
SELECT * FROM flags;
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| ID | Student_ID | Staff_ID | Datetime | Period_ID | Category_ID | Action_Taken_Category_ID | Action_Taken_Status |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| 8843 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8844 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8845 | 139027 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8846 | 139041 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8847 | 139041 | 34961 | 2014-03-11 09:01:02 | 2 | 12 | 26 | P |
| 8848 | 139041 | 34996 | 2014-03-11 09:23:21 | 3 | 12 | 27 | C |
| 8849 | 139041 | 35022 | 2014-03-11 11:07:46 | 4 | 34 | 28 | P |
| 8850 | 139892 | 138439 | 2014-03-11 11:12:47 | 4 | 21 | 7 | C |
| 8851 | 138832 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
| 8852 | 34533 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
SELECT * FROM categories;
+----+------+--------------------------------------+--------+----------+
| ID | Type | Title | Weight | Added_By |
+----+------+--------------------------------------+--------+----------+
| 10 | F | Low level disruption | 1 | NULL |
| 11 | F | Swearing directly at another student | 2 | NULL |
| 12 | F | Late | 1 | NULL |
| 13 | F | Absconded | 3 | NULL |
| 14 | F | Refusal to follow instruction | 3 | NULL |
| 15 | F | Smoking | 2 | NULL |
| 16 | F | No homework | 2 | NULL |
| 17 | F | Disruptive outside classroom | 2 | NULL |
| 18 | F | Eating/drinking in lesson | 1 | NULL |
| 19 | F | Incorrect uniform/equipment | 1 | NULL |
| 20 | F | Phone out in lesson | 3 | NULL |
| 21 | F | Aggressive defiance | 4 | NULL |
| 22 | F | Missed detention | 3 | NULL |
| 23 | F | Inappropriate behaviour/comments | 3 | NULL |
| 32 | F | IT Misuse | NULL | NULL |
| 34 | F | Inappropriate attitude towards staff | NULL | NULL |
| 35 | F | Care & Guidance | NULL | NULL |
+----+------+--------------------------------------+--------+----------+
SELECT * FROM transactions;
+----------------+------------+----------+--------------+--------+-------------+
| Transaction_ID | Datetime | Giver_ID | Recipient_ID | Points | Category_ID |
+----------------+------------+----------+--------------+--------+-------------+
| 34 | 2011-09-07 | 35019 | 137608 | 2 | 1 |
| 35 | 2011-09-07 | 35019 | 139027 | 2 | 1 |
| 36 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 37 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 38 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 39 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 40 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 41 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 42 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 43 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 44 | 2011-09-07 | 35006 | 137434 | 2 | 1 |
| 45 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 46 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 47 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 48 | 2011-09-07 | 35023 | 137608 | 5 | 2 |
| 49 | 2011-09-07 | 35023 | 139027 | 5 | 2 |
| 50 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 51 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 52 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 53 | 2011-09-07 | 35023 | 137608 | 5 | 3 |
+----------------+------------+----------+--------------+--------+-------------+
SELECT id,UPN,Year_Group,Tutor_Group,SEN_Status,Flags FROM student;
+--------+---------------+------------+-------------+------------+--------+
| id | UPN | Year_Group | Tutor_Group | SEN_Status | Flags |
+--------+---------------+------------+-------------+------------+--------+
| 137608 | A929238400044 | 11 | 11VID | A | |
| 139027 | A929238401045 | 10 | 10KS | | |
| 139041 | A929238402017 | 10 | 10RJ | A | FSM |
| 139892 | A929238403018 | 9 | 9BW | | |
| 139938 | A929238403020 | 9 | 9RH | | |
| 137434 | A929238500027 | 11 | 11VID | | |
| 138832 | A929238502002 | 10 | 10RY | A | FSM,PA |
| 34533 | A929238599028 | 0 | | | PA |
| 139564 | A929241500025 | 12 | | | PA |
| 90306 | A929253100006 | 12 | SLH | A | PA |
+--------+---------------+------------+-------------+------------+--------+

Mysql query to convert table from long format to wide format

I have a table called ContactAttrbiutes which contains a list of each contacts' attributes. The kind of data stored for these contacts include: Title, Forename, Surname telephone number etc.
Current Table
+-------------+-----------+------------------------------+
| attributeId | ContactId | AttributeValue |
+-------------+-----------+------------------------------+
| 1 | 5 | Lady |
| 2 | 5 | Elizabeth |
| 3 | 5 | E |
| 4 | 5 | Anson |
| 5 | 5 | |
| 6 | 5 | |
| 7 | 5 | |
| 8 | 5 | |
| 10 | 5 | 0207 72776 |
| 11 | 5 | |
| 12 | 5 | 0207 22996 |
| 13 | 5 | 0207 72761 |
| 14 | 5 | |
| 15 | 5 | |
| 60 | 5 | Lloyds |
| 61 | 5 | |
| 1 | 10 | Mr |
| 2 | 10 | John |
| 3 | 10 | J C |
| 4 | 10 | Beveridge |
| 5 | 10 | Esq QC |
| 6 | 10 | Retired |
| 7 | 10 | |
| 8 | 10 | |
| 10 | 10 | 0207 930 |
| 11 | 10 | |
| 12 | 10 | |
| 13 | 10 | 0207 930 |
| 14 | 10 | |
| 15 | 10 | |
| 60 | 10 | |
| 61 | 10 | |
+-------------+-----------+------------------------------+
However I would like to run a query to create a table that looks like...
New Table
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| ContactId | AttributeValue_Title | AttributeValue_ForeName |AttributeValue_Initial | AttributeValue_Surname |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 5 | Lady | Elizabeth | E | Anson |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 10 | Mr | John | J C | Beveridge |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
I am sure there is a very simple answer but I have spent hours looking. Can anyone help?
The above is only a small extract of my table, I have 750,000 contacts. In addition I would like the final table to have more columns than I have described above but they will come from different Attributes with the existing table.
Thank you very much in advance.
try this
SELECT ContactId ,
max(CASE when attributeId = 1 then AttributeValue end) as AttributeValue_Title ,
max(CASE when attributeId = 2 then AttributeValue end )as AttributeValue_ForeName ,
max(CASE when attributeId = 3 then AttributeValue end )as AttributeValue_Initial ,
max(CASE when attributeId = 4 then AttributeValue end) as AttributeValue_Surname
from Table1
group by ContactId
DEMO HERE
if you want to make your result more longer for other attributeId then just add a case statment as in the code.
SELECT
t_title.AttributeValue AS title,
t_name.AttributeValue AS name,
...
FROM the_table AS t_title
JOIN the_table AS t_firstname USING(contact_id)
JOIN ...
WHERE
t_title.attributeId = 1 AND
t_firstname.attributeId = 2 AND
...
EAV "model" is an antipattern in most cases. Are you really going to have a variable number of attributes? If yes, then no-SQL solution might be more appropriate than a relational database.