I have the following mysql tables(tests,questions) with the corresponding columns types. The field correct_answer of questions table can hold a value equal to 'yes' or 'no'. When it is 'yes', it is counted as correct. When it is 'no',it is counted as incorrect. The fields correct and incorrect in table tests holds the sum of those counts. I had wanted a single sql command that does the update of the tests table based on the values in questions table. A record is initially inserted in tests table with the counts put to 0 while the table questions is filled up progressively.
tests(test_id integer primary key, correct integer, incorrect integer)
questions(test_id integer foreign key, question varchar(35), correct_answer varchar(3))
Test data
tests
10,0,0
11,0,0
questions
10,'textbook','yes'
10,'fire','no'
10,'card','yes'
11,'lamp','yes'
After I run the sql command, the tests table must read:
10,2,1
11,1,0
I tried "update tests set correct=select count(test_id) from questions where correct_answer='oui',incorrect=select count(test_id) from questions where correct_answer='non'" but does not work
you can do the aggregation inside a subquery and do a join with tests table to update the total counts
update tests t
join ( select test_id,
sum(correct_answer='yes') as correctCount,
sum(correct_answer='no') as incorrectCount
from questions
group by test_id) aggr
on t.test_id = aggr.test_id
set t.correct = aggr.correctCount,
t.incorrect = aggr.incorrectCount
Try this.
UPDATE tests
SET
tests.correct = (
SELECT count(*) FROM questions WHERE tests.test_id = questions.test_id AND questions.correct_answer = 'yes' GROUP BY test_id
),
tests.incorrect = (
SELECT count(*) FROM questions WHERE tests.test_id = questions.test_id AND questions.correct_answer = 'no' GROUP BY test_id
)
Related
Definitely a basic question, but I couldn't find an example.
I'm writing a procedure which merges two rows into the good row. It moves all child rows' ids to being the correct one, replaces all NULL values with available values in the row being removed before finally deleting the 'bad' row.
What I have so far is this:
CREATE DEFINER=`danielv`#`%`
PROCEDURE `emp_merge`(IN `#core_emp_id` int, IN `#bad_emp_id` int)
BEGIN
UPDATE claim SET employee_id = #core_emp_id
WHERE employee_id = #bad_emp_id;
WITH bad_employee_values AS (
SELECT * FROM employee WHERE employee_id = #bad_emp_id
)
UPDATE employee SET
employee.employment_date = COALESCE(employee.employment_date, bad_employee_values.employment_date),
WHERE employee_id = #core_emp_id;
DELETE FROM employee WHERE employee_id = #bad_emp_id;
END
However, I'm getting non-descript error messages and I'm not sure why. I suspect there's an issue with how I'm handling my CTE and coalesce function, but I'm not sure where the gap in my understanding is.
In this statement :
WITH bad_employee_values AS (SELECT * FROM employee WHERE employee_id = #bad_emp_id)
UPDATE employee SET
employee.employment_date = COALESCE(employee.employment_date, bad_employee_values.employment_date),
WHERE employee_id = #core_emp_id;
You are defining CTE bad_employee_values but you are not using it in the UPDATE part of the query, hence you cannot access its columns : for MySQL, bad_employee_values.employment_date is unknown.
It looks like you could simply avoid a CTE here. You could just self-join the table, like so :
UPDATE employee e_core
INNER JOIN employee e_bad ON e_bad.employee_id = #bad_emp_id
SET e_core.employment_date = e_bad.employment_date,
WHERE employee_id = #core_emp_id AND e_core.employment_date IS NULL
This query will simply select the record identified by #core_emp_id, join it with the corresponding "bad" record, and copy the value of employment_date. The second condition in the WHERE clause prevents records whose employment_date is not null from being selected.
I have inherited a table with a field "sku" with should be unique, but thanks to a failing sku-generating method is now littered with dozens of duplicates all around.
I need to quickly fix these duplicates (other parts of the application are failing when encountering these duplicate records) by running an update and appending the record ID to the SKU (which is a valid solution for the time being for this application).
I'm trying to run:
UPDATE
main_product_table
SET sku = CONCAT(sku, '-', CAST(product_id as CHAR) )
WHERE sku IN (
SELECT sku FROM main_product_table
GROUP BY sku
HAVING COUNT(*) > 1
);
But I receive:
You can't specify target table 'main_product_table' for update in FROM clause
Is there a way to accomplish the same? Is mysql complaining about me having main_product_table both in the update and in the subquery to get the duplicates?
Thanks!
Try this:
UPDATE
main_product_table
SET sku = CONCAT(sku, '-', CAST(product_id as CHAR) )
WHERE sku IN (
select * from ( SELECT sku FROM main_product_table
GROUP BY sku
HAVING COUNT(*) > 1) as p
);
Added table alias in inner query.
I'm trying to write a query like
if (select count(*) from Users where fkId=5000 and status='r') =
(select count(*) from Users where fkId=5000) then ..
in just one query.
What this means is, if all the rows that have fkId=5000 also have status=r, then do something.
There can be any number of rows with fkId=5000, and any fraction of those rows could have status=r, status=k, status=l, status=a etc. I'm interested in the case where ALL the rows that have fkId=5000 also have status=r (and not any other status).
The way I'm doing it now is
how many rows with id=5000 and status = 'r'?
how many rows with id=5000?
are those numbers equal? then ..
I'm trying to figure out how to rewrite this query using only 1 query, instead of 2. Keyword ALL didn't seem to be able to write such a query (<> ALL is equivalent to NOT IN). I tried a couple of GROUP BY formulations but could not get the correct result to appear.
The most efficient way to do this is:
if not exists (select 1
from users
where fkid = 5000 and (status <> 'r' or status is null)
)
This will stop the query at the first non-matching row.
I suggest you to check for any rows with status not equal to 'r'
SELECT count(*)>0 FROM Users WHERE fkId = 5000 AND status != 'r'
In the following case, if the number 1 is "true" (which it is) then you'll get Yes back, and if not you'll get No back:
SELECT IF(1, 'Yes', 'No') AS yesorno
(Go ahead -- try it!)
In your case however, the following would be more appropriate:
SELECT IF (
(SELECT COUNT(*) FROM Users WHERE fkId=5000 AND status IN('r') AND status NOT IN('1', 'a', 'k')) = (SELECT COUNT(*) FROM Users WHERE fkId=5000),
'They are equal.',
'They are not equal.'
)
AS are_they_equal
By adding AS, you can manipulate the name of the "column" that's returned to you.
Hope that helps... Also, see this page if you'd like more info.
:)
EASY!
Simply join back to the same table. Here is the complete code for testing:
CREATE TABLE Users(id int NOT NULL AUTO_INCREMENT, fkID int NOT NULL, status char(1), PRIMARY KEY (id));
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
-- The next query produces "0" to indicate no miss-matches
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
-- now change one record to create a miss-match
UPDATE Users SET status='l' WHERE id=3 ;
-- The next query produces "1" to indicate 1 miss-match
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
DROP TABLE Users;
So all you need to test for in the result is that it's 0 (zero) meaning everything has fkID=5000 also has status='r'
If you properly index your table then joining back to the same table is not an issue and certainly beats having to do a 2nd query.
Besides the NOT EXISTS version - which should be the most efficient as it does no counting at all and exits as soon as it finds a value that doesn't match the conditions, there is one more way, that will work if status is not nullable and will be efficient if there is an index on (fkId, status):
IF EXISTS
( SELECT 1
FROM Users
WHERE fkId = 5000
HAVING MIN(status) = 'r'
AND MAX(status) = 'r'
)
There is one difference though. The above will show false if there are no rows at all with fkId=5000, while the NOT EXISTS version will show true - which is probably what you want anyway.
I have a table defined like the following...
CREATE table actions (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
end BOOLEAN,
type VARCHAR(15) NOT NULL,
subtype_a VARCHAR(15),
subtype_b VARCHAR(15),
);
I'm trying to query for the last end action of some type to happen on each unique (subtype_a, subtype_b) pair, similar to a group by (except SQLite doesn't say what row is guaranteed to be returned by a group by).
On an SQLite database of about 1MB, the query I have now can take upwards of two seconds, but I need to speed it up to take under a second (since this will be called frequently).
example query:
SELECT * FROM actions a_out
WHERE id =
(SELECT MAX(a_in.id) FROM actions a_in
WHERE a_out.subtype_a = a_in.subtype_a
AND a_out.subtype_b = a_in.subtype_b
AND a_in.status IS NOT NULL
AND a_in.type = "some_type");
If it helps, I know all the unique possibilities for a (subtype_a,subtype_b)
eg:
(a,1)
(a,2)
(b,3)
(b,4)
(b,5)
(b,6)
Beginning with version 3.7.11, SQLite guarantees which record is returned in a group:
Queries of the form: "SELECT max(x), y FROM table" returns the value of y on the same row that contains the maximum x value.
So greatest-n-per-group can be implemented in a much simpler way:
SELECT *, max(id)
FROM actions
WHERE type = 'some_type'
GROUP BY subtype_a, subtype_b
Is this any faster?
select * from actions where id in (select max(id) from actions where type="some_type" group by subtype_a, subtype_b);
This is the greatest-in-per-group problem that comes up frequently on StackOverflow.
Here's how I solve it:
SELECT a_out.* FROM actions a_out
LEFT OUTER JOIN actions a_in ON a_out.subtype_a = a_in.subtype_a
AND a_out.subtype_b = a_in.subtype_b
AND a_out.id < a_in.id
WHERE a_out.type = "some type" AND a_in.id IS NULL
If you have an index on (type, subtype_a, subtype_b, id) this should run very fast.
See also my answers to similar SQL questions:
Fetch the row which has the Max value for a column
Retrieving the last record in each group
SQL join: selecting the last records in a one-to-many relationship
Or this brilliant article by Jan Kneschke: Groupwise Max.
I have the task to repair some invalid data in a mysql-database. In one table there are people with a missing date, which should be filled from a second table, if there is a corresponding entry.
TablePeople: ID, MissingDate, ...
TableEvent: ID, people_id, replacementDate, ...
Update TablePeople
set missingdate = (select replacementDate
from TableEvent
where people_id = TablePeople.ID)
where missingdate is null
and (select count(*)
from TableEvent
where people_id = TablePeople.ID) > 0
Certainly doesn't work. Is there any other way with SQL? Or how can I process single rows in mysql to get it done?
We need details about what's not working, but I think you only need to use:
UPDATE TablePeople
SET missingdate = (SELECT MAX(te.replacementDate)
FROM TABLEEVENT te
WHERE te.people_id = TablePeople.id)
WHERE missingdate IS NULL
Notes
MAX is being used to return the latest replacementdate, out of fear of risk that you're getting multiple values from the subquery
If there's no supporting record in TABLEEVENT, it will return null so there's no change