Struggling to do this with one query:
scores
==========
| child_name | week | behaviour_score | test | test_score |
+---------------------------------------------------------+
| jo | 1 | 75 | 1 | 55 |
| jo | 1 | 75 | 2 | 54 |
| jo | 2 | 71 | 1 | 65 |
| jo | 3 | 68 | 1 | 70 |
| jo | 3 | 68 | 2 | 74 |
| jo | 3 | 68 | 3 | 45 |
Each child takes 1 to n tests each week. Behaviour is recorded as a value for the week, but is stored in each row - so e.g. jo's behaviour for week 1 is 75, and for week 3 is 68. It's not normalised. I know. Wasn't my call.
The table contains records like this for many children.
I am trying to return 1 row per child, with name, sum of behaviour, and sum of test scores.
But because behaviour isnt normalised it must only count the value once per week.
So sum of behaviour above is 75+71+68
And sum of test scores is 55+54+65+70+74+45
My first attempt was
SELECT
child_name,
SUM(behaviour_score),
SUM(test_Score)
FROM results
GROUP BY child_name
But of course, that gives too large a value for behaviour score as it doesnt account for the values being duplicated for a given week.
Is it possible to achieve this with one query?
You need to first reduce the data into one row per child, per week with the total test score but only one (let's take the MAX) behavior score), then base your query on that:
SELECT
child_name,
SUM(behaviour_score),
SUM(test_Score)
FROM (
SELECT child_name, MAX(behavior_score), SUM(test_Score)
FROM result GROUP BY child_name, week
) AS OnePerWeek
GROUP BY child_name
Related
I am new to SQL, would like to have your suggestions on how to solve this problem,
I have the sales information by type
I want to sum the Prices of certain references by Type and based on the resulting sum, fetch the values from another table and populate in the Output Column.
Group Type 100000 200000 300000
1 A 1 2 3
1 B 0 1 1
2 T 2 2 4
2 U 0 2 2
3 V 2 2 3
4 N 1 1 1
From the above table 2 we find the TYPE A and B belong to same group - Group 1. So in the first table, the query should sum Prices of the references belonging to the Group 1. If the sum is >100000 and <=200000 then based on the type the corresponding value must be chosen.
Incase the sum of Prices based on group is less than 100000 or the type not found in Table 2 then it should take the values from the below table
[+------+----+---+
| Type | 1 | 2 |
+------+----+---+
| A | 50 | 2 |
| B | 60 | 5 |
| C | 65 | 2 |
| D | 65 | 3 |
| E | 65 | 4 |
+------+----+---+][3]
Thus the final output for the above datasheet would be like below,
Order ID Reference Type Price Output
101 AAA A 500000 3
101 AAB B 100000 1
101 ABC C 20000 67
101 DCE B 50000 1
101 BOD D 200000 68
101 ZYZ E 200000 69
102 AAA A 20000 52
So for the first line, its TYPE A and Type A is present under Group 1 and in Group1 we also have Type 2. So for the same order ID 101 , the overall Sales of Type A and B is 650000 > 300000, therefore for Type A we chose the value 3 from the table 2. Since Type C is not present in Table 2, I went to Table 3 and added the two values and so on
Sorry for the long post. Hope my question is clear? Would like to have your expert opinion.
Thanks,
SS
Join all tables and make sure you do LEFT JOIN as we want to keep records from the first table even we don't have corresponding data in the second or third table.
For total count, give priority to the second table, use case when to verify in which range this mrp field is falling. If lies within a range pick count from the second table otherwise pick count from the third table.
SELECT
s.order_id,
s.reference,
s.`type`,
s.mrp,
#a:= IFNULL(g_total.Total, s.mrp) AS MRP_Total, -- #a variable to use it in CASE WHEN clause
CASE
WHEN #a > 100000 AND #a <= 200000 AND sg.`type` IS NOT NULL THEN sg.price_100000
WHEN #a > 200000 AND #a <= 300000 AND sg.`type` IS NOT NULL THEN sg.price_200000
WHEN #a > 300000 AND sg.`type` IS NOT NULL THEN sg.price_300000
ELSE tp.price_1 + tp.price_2
END Total
FROM sales s
LEFT JOIN sales_group sg ON s.`type` = sg.`type`
LEFT JOIN type_prices tp ON s.`type` = tp.`type`
LEFT JOIN (
SELECT
s.order_id, sgg.`group`, SUM(mrp) as Total
FROM sales s
INNER JOIN sales_group sgg ON s.`type` = sgg.`type`
GROUP BY s.order_id, sgg.`group`
) AS g_total -- Temp table to find total MRP, order and group wise
ON s.order_id = g_total.order_id AND sg.`group` = g_total.`group`
ORDER BY s.order_id, s.`type`;
Output:
sales
---
| order_id | reference | type | mrp | MRP_Total | Total |
---------------------------------------------------------
| 101 | AAA | A | 500000 | 650000 | 3 |
| 101 | DCE | B | 50000 | 650000 | 1 |
| 101 | AAB | B | 100000 | 650000 | 1 |
| 101 | ABC | C | 200000 | 200000 | 67 |
| 101 | BOD | D | 200000 | 200000 | 68 |
| 101 | ZYZ | E | 200000 | 200000 | 69 |
| 102 | AAA | A | 20000 | 20000 | 52 |
Note: sg.type IS NOT NULL is added in CASE WHEN clause because if we don't have any mapping in the second table, we should move to ELSE part which refers to the third table.
An interesting SQL query CHALLENGE:
A table named athelets consisting of id, ath_id, name, score, date.
+----+--------+-----------------+--------+------------+
| id | ath_id | name | record | r_date |
+----+--------+-----------------+--------+------------+
| 1 | 2 | John Wayne | 79 | 2010-07-08 |
| 2 | 7 | Ronald Regan | 51 | 2000-03-22 |
| 3 | 1 | Ford Harrison | 85 | 2009-11-13 |
| 4 | 2 | John Wayne | 69 | 2017-01-01 |
Please write a sql query to list the average value of the top three scores of each athlete, something like:
ath_id: 1, the arithmetic mean of his/her top 3 records: 77
ath_id: 2, the arithmetic mean of his/her top 3 records: 73
ath_id: 3, the arithmetic mean of his/her top 3 records: 47
select ath_id, avg(record)
from
(select ath_id, record
from atheletes as t1
where
(select count(*) from atheletes where t1.ath_id=ath_id and record > t1.record) < 3) as d
group by ath_id;
The above query should works as expected.
Assuming combinations of athletes and records are unique...
SELECT ath_id
, ROUND(AVG(record),2) top3_avg
FROM
( SELECT x.*
FROM athletes x
JOIN athletes y
ON y.ath_id = x.ath_id
AND y.record >= x.record
GROUP
BY x.id
HAVING COUNT(*) <=3
) a
GROUP
BY ath_id;
I have a table of routines. In this table, I have the column "grade" (which is not mandatory), and the column "date". Also, I have a number of days and an array of ids of users. I need a query that returns me the last routine that have a value != null for "grade" column and datediff(current_date,date) >= number_of_days for each id in the array and make an average of all these values.
e.g.
today = 2014/10/15
number_of_days = 10
ids(1,3)
routines
id | type | date | grade | user_id
1 | 1 | 2014-10-10 | 3 | 1
2 | 1 | 2014-10-04 | 3 | 1
3 | 1 | 2014-10-01 | 3 | 1
4 | 1 | 2014-09-24 | 2 | 1
5 | 1 | 2014-10-10 | 2 | 2
6 | 1 | 2014-10-04 | 3 | 2
7 | 1 | 2014-10-01 | 3 | 2
8 | 1 | 2014-09-24 | 1 | 2
9 | 1 | 2014-10-10 | 1 | 3
10 | 1 | 2014-10-04 | 1 | 3
11 | 1 | 2014-10-01 | 1 | 3
12 | 1 | 2014-09-24 | 1 | 3
In this case, my query would return an avg between "grade" of row id #2 and #10
I think you're saying that you want to consider rows having non-null values in the grade column, a date within a given number of days of the current date, and one of a given set of user_ids. Among those rows, for each user_id you want to choose the row with the latest date, and compute an average of the grade columns for those rows.
I will assume that you cannot have any two rows with the same user_id and date, both with non-null grades, else the question you want to ask does not have a well-defined answer.
A query along these lines should do the trick:
SELECT AVG(r.grade) AS average_grade
FROM
(SELECT user_id, MAX(date) AS date
FROM routines
WHERE grade IS NOT NULL
AND DATEDIFF(CURDATE(), date) >= 10
AND user_id IN (1,3)
GROUP BY user_id) AS md
JOIN routines r
ON r.user_id = md.user_id AND r.date = md.date
Note that in principle you need a grade IS NOT NULL condition on both the inner and the outer query to select the correct rows to average, but in practice AVG() ignores nulls, so you don't actually have to filter out the extra rows in the outer query.
I have two tables. One table stores basic information on inventory items like id and description. The other table stores the status of the inventory items at the end of each day; so there are many rows in this table with the same inventoryItemID but different dates. Below is basically what my tables look like.
TABLE: listItems
|itemRecordNumber|itemName|minStock|reorderQty|note|
| 510 | bag | 10 | 20 | |
| 511 | ball | 20 | 40 | |
| 512 | shoe | 09 | 10 | xx |
,
TABLE: inventoryTrans
|itemRecordNumber|recordType|quantity| trans_date |
| 510 | 50 | 30 | 09/12/2013 |
| 510 | 40 | 33 | 09/12/2013 |
| 510 | 50 | 35 | 08/12/2013 |
| 510 | 40 | 35 | 08/12/2013 |
| 511 | 50 | 10 | 08/12/2013 |
| 511 | 40 | 15 | 09/12/2013 |
| 512 | 50 | 33 | 07/12/2013 |
| 512 | 40 | 34 | 09/12/2013 |
Now, what i want is, the most recent record from the table inventoryTrans for each itemRecordNumber and only the records of type 50(a sale), but wait theres more, if the note in the table listItems on an item is 'xx' i am not interested in that result. What i really need from this table is the quantity. Notice this table does not have the items name / minStock / reorderQty , which is why i need the first table listItems, to get that info.
So in a single query i would like to get,
|itemRecordNumber|itemName| trans_date |quantity|minStock|reorderQty|
| 510 | bag | 09/12/2013 | 30 | 10 | 20 |
| 511 | ball | 08/12/2013 | 10 | 20 | 40 |
So what i want is this information. Notice it is the most recent of the recordType '50' for each ID, yet item 512 was excluded due to the note on the item being 'xx' in listItems.note ;
Now before you criticize the construct of the tables, it is not something I can change. This is how the tables for Peachtree accounting(the software my company at hand is using) is layed out, so this is what I have to work with. The goal is to generate custom information on inventory for better management.
Now how can I construct a SQL query for this. i have seen people use LEFT JOIN in some other similar examples but i cant get it to work.
Here we go
http://sqlfiddle.com/#!2/d30823/1 this is the example with your data
SELECT a.itemRecordNumber,
a.itemName,
max(b.trans_date) AS trans_date,
b.quantity,
a.minStock,
a.reorderQty
FROM listItems a,
inventoryTrans b
WHERE a.itemRecordNumber = b.itemRecordNumber
AND a.note<>'xx'
AND b.recordtype=50
GROUP BY a.itemRecordNumber
Edit:
In case of MS Access (which I am unable to test), this should work, can you try (I did try in MS SQL which is the closest I can test in SQLFiddle
http://sqlfiddle.com/#!3/d30823/2
SELECT a.itemRecordNumber,
a.itemName,
max(b.trans_date) AS trans_date,
b.quantity,
a.minStock,
a.reorderQty
FROM listItems a,
inventoryTrans b
WHERE a.itemRecordNumber = b.itemRecordNumber
AND a.note<>'xx'
AND b.recordtype=50
AND b.trans_date =
(SELECT max(trans_date)
FROM inventoryTrans c
WHERE c.itemRecordNumber = b.itemRecordNumber
AND c.recordtype=50)
GROUP BY a.itemRecordNumber,
a.itemName,
b.quantity,
a.minStock,
a.reorderQty
Let me know if it does not work for you for any reason, I got the same results as your question
Though this is not the most efficient way, it should work.
SELECT l.*, i.trans_date, i.quantity
FROM listItems as l
JOIN (SELECT i.itemRecordNumber, MAX(trans_date) as date
FROM inventoryTrans as i GROUP BY i.itemRecordNumber
) as x ON x.itemRecordNumber = l.itemRecordNumber
JOIN inventoryTrans as i ON l.itemRecordNumber = i.itemRecordNumber
AND i.trans_date = x.date
WHERE i.recordType = 50
Though this will give a row for itemRecordNumber = 512 too, if you do not need it you can add a WHERE and filter according to your need.
I have a table containing the following fields:
date, time, node, result
describing some numeric result for different nodes at different dates and times throughout each day. Typical listing will look something like this:
date | time | node | result
----------------------------------
2011-03-01 | 10:02 | A | 10
2011-03-01 | 11:02 | A | 20
2011-03-02 | 03:13 | A | 23
2011-03-02 | 12:15 | A | 18
2011-03-02 | 13:15 | A | 8
2011-03-01 | 13:12 | B | 2
2011-03-01 | 14:26 | B | 1
2011-03-02 | 08:00 | B | 6
2011-03-02 | 07:22 | B | 3
2011-03-02 | 21:19 | B | 4
I want to form a query that'll get the last result from each day for each node, such that I'd get something like this:
date | time | node | latest
-----------------------------------
2011-03-01 | 11:02 | A | 20
2011-03-01 | 14:26 | B | 1
2011-03-02 | 13:15 | A | 8
2011-03-02 | 21:19 | B | 4
I thought about doing a group by date, node, but then extracting the last value was a mess (I used group_concat( result order by time ) and used SUBSTRING() to get the last value. Baah, I know). Is there a simple way to do this in mysql?
I'm pretty sure I saw a similar request solving it very nice without using an INNER JOIN but I can't find it right now (and it might have been SQL Server) but following should work nevertheless.
SELECT n.*
FROM Nodes n
INNER JOIN (
SELECT MAX(time) AS Time
, Date
, Node
FROM Nodes
GROUP BY
Date
, Node
) nm ON nm.time = n.time
AND nm.Date = n.Date
AND nm.Node = n.Node
I would think that you would have to use something like the Max() function. Sorry I don't have mysql, so I can't test but I would think something like this
select t.date, t.node, t.latest, Max(time) from Table t Group By t.node, t.date
I think the aggregate function will return only the one row per grouping.