I have a rather complex SQL query that I am looking for a little help with.
I have two tables: a history table and a details table.
The history table contains the following columns.
Event Date(ev_date)
Event Code(ev_code)
Machine ID(mc_id)
The Details table contains the following columns:
Machine ID(mc_id),
Location ID(lo_id)
Machine Name(mc_name)
I need a query that returns the count of the number of events from the history table between a given date range of a given group of machines given by Location ID.
So, kinda in sudo code:
SELECT COUNT(*)
FROM history
WHERE ev_date (BETWEEN start_date AND end_date) AND ev_code = 1 AND ????
(mc_id must have certain lo_id from details table).
Does this make sense?
Thanks
SELECT COUNT(*)
FROM history h
WHERE h.ev_date BETWEEN #start_date AND #end_date
AND ev_code = 1
AND EXISTS(SELECT NULL
FROM details d
WHERE h.mc_id = d.mc_id
AND d.lo_id = #LocationID);
Assuming you have a one-to-one mapping between history and details on mc_id:
SELECT COUNT(*)
FROM history h
JOIN details d USING mc_id
WHERE h.ev_code = 1
AND h.ev_date between start_date and end_date
AND d.lo_id IN (?, ?, ?, ...)
Alternatively ON h.mc_id = d.mc_id instead of USING mc_id.
Related
I've accidentally managed to add duplicate entries into my database. The database contains a list of telephone numbers and they are routed via the information contained in the value field. The id field is unique per entry, and the UUID and username fields should be identical but shouldn't exist in the table more than once.
Data has been blanked in the screenshot for data protection.
The following command allowed me to identify I had duplicate entries which can be seen in the screenshot above.
select * uuid, count(*) from usr_preferences group by uuid having count(*) > 1;
I'm after some help on how I could delete entries where the UUID count is more than one but one entry must remain. deleting the duplicate UUID with the highest id number would be preferred.
Is there a way to display the results before deleting them?
MySQL version - mysql Ver 14.14 Distrib 5.7.38-41, for Linux (x86_64) using 6.2
Thanks
Could you give the following bit of code a go? Please make sure you have the database backed up before running this.
DELETE b FROM `test` a, `test` b where b.uuid = a.uuid and b.id > a.id;
I've expanded on your text data to make sure it will remove both duplicates and triplicates leaving the lowest ID. You can find my testing at this DB Fiddle.
https://www.db-fiddle.com/f/sUr6V6UP9tZ1Ya8eESid33/0
Hope this sorts you issue.
Try the following for MySQL v5.7:
set #rn=0;
set #uuid=null;
delete from usr_preferences where id in
(
select D.id
from
(
select id, uuid,
case
when #uuid <> uuid then
#rn:=1
else
#rn:=#rn+1
end as rn,
#uuid:=uuid
from usr_preferences order by id,uuid
) D
where D.rn>1
);
Select * From usr_preferences;
See a demo from db-fiddle.
Important Note:
Test the query before using it on your table, and take a backup of your table before running this query on it.
For MySQL v8.0 and above you may try the following:
with cte as
(
select id, row_number() over (partition by uuid order by id) as rn
from usr_preferences
)
delete U From
usr_preferences U join cte C
On U.id = C.id
where C.rn > 1;
I have below query in mysql where I want to check if branch id and year of finance type from branch_master are equal with branch id and year of manager then update status in manager table against branch id in manager
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
(
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)
)
)
but getting error
Table 'm1' is specified twice, both as a target for 'UPDATE' and as a
separate source for data
This is a typical MySQL thing and can usually be circumvented by selecting from the table derived, i.e. instead of
FROM manager AS m2
use
FROM (select * from manager) AS m2
The complete statement:
UPDATE manager
SET status = 'Y'
WHERE branch_id IN
(
select branch_id
FROM (select * from manager) AS m2
WHERE (branch_id, year) IN
(
SELECT branch_id, year
FROM branch_master
WHERE type = 'finance'
)
);
The correct answer is in this SO post.
The problem with here accepted answer is - as was already mentioned multiple times - creating a full copy of the whole table. This is way far from optimal and the most space complex one. The idea is to materialize the subset of data used for update only, so in your case it would be like this:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT * FROM(
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance')
) t
)
Basically you just encapsulate your previous source for data query inside of
SELECT * FROM (...) t
Try to use the EXISTS operator:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE EXISTS (SELECT 1
FROM (SELECT m2.branch_id
FROM branch_master AS bm
JOIN manager AS m2
WHERE bm.type = 'finance' AND
bm.branch_id = m2.branch_id AND
bm.year = m2.year) AS t
WHERE t.branch_id = m1.branch_id);
Note: The query uses an additional nesting level, as proposed by #Thorsten, as a means to circumvent the Table is specified twice error.
Demo here
Try :::
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
(SELECT DISTINCT branch_id
FROM branch_master
WHERE type = 'finance'))
AND m1.year IN ((SELECT DISTINCT year
FROM branch_master
WHERE type = 'finance'))
The problem I had with the accepted answer is that create a copy of the whole table, and for me wasn't an option, I tried to execute it but after several hours I had to cancel it.
A very fast way if you have a huge amount of data is create a temporary table:
Create TMP table
CREATE TEMPORARY TABLE tmp_manager
(branch_id bigint auto_increment primary key,
year datetime null);
Populate TMP table
insert into tmp_manager (branch_id, year)
select branch_id, year
from manager;
Update with join
UPDATE manager as m, tmp_manager as tmp_m
inner JOIN manager as man on tmp_m.branch_id = man.branch_id
SET status = 'Y'
WHERE m.branch_id = tmp_m.branch_id and m.year = tmp_m.year and m.type = 'finance';
This is by far the fastest way:
UPDATE manager m
INNER JOIN branch_master b on m.branch_id=b.branch_id AND m.year=b.year
SET m.status='Y'
WHERE b.type='finance'
Note that if it is a 1:n relationship the SET command will be run more than once. In this case that is no problem. But if you have something like "SET price=price+5" you cannot use this construction.
Maybe not a solution, but some thoughts about why it doesn't work in the first place:
Reading data from a table and also writing data into that same table is somewhat an ill-defined task. In what order should the data be read and written? Should newly written data be considered when reading it back from the same table? MySQL refusing to execute this isn't just because of a limitation, it's because it's not a well-defined task.
The solutions involving SELECT ... FROM (SELECT * FROM table) AS tmp just dump the entire content of a table into a temporary table, which can then be used in any further outer queries, like for example an update query. This forces the order of operations to be: Select everything first into a temporary table and then use that data (instead of the data from the original table) to do the updates.
However if the table involved is large, then this temporary copying is going to be incredibly slow. No indexes will ever speed up SELECT * FROM table.
I might have a slow day today... but isn't the original query identical to this one, which souldn't have any problems?
UPDATE manager as m1
SET m1.status = 'Y'
WHERE (m1.branch_id, m1.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)
I have an event occurring once a day. I have 2 tables:
application
rating
Basically, each application has an avg_score that is given by the average of all the feedbacks given by users that are stored in the table rating in the field score. I wrote an event that once a day refresh this value:
CREATE EVENT MY_DAILY_UPDATE
ON SCHEDULE EVERY 1 DAY STARTS '2011-07-23 23:30:00'
DO
UPDATE application
SET `avg_score`= (SELECT AVG(`score`) as new_score
FROM `rating`
WHERE `ID_APPLICATION` = 1)
WHERE `APPLICATION_ID` = 1
It works, but only for the application with ID = 1, cause i wrote it by myself.
Instead i need my query to update the field avg_score for each application in the table application.
So i think i need to change the value 1 with a variable ID (ex WHERE APPLICATION_ID = ID_VARIABLE).......and this variable should take the id value of each app in the application table (1,2,3.....4 etc).......but i have no idea about how to change my query.....
Change your sub-query to referrence the values in the outer query. (This makes it a correlated sub-query.)
UPDATE application
SET avg_score = (
SELECT AVG(score)
FROM rating
WHERE ID_APPLICATION = application.APPLICATION_ID
)
Alternatively, as you're doing this for "all values", just join on the sub-query...
UPDATE
application
INNER JOIN
(
SELECT ID_APPLICATION, AVG(score) AS score FROM rating GROUP BY ID_APPLICATION
)
AS averages
ON averages.ID_APPLICAITON = application.APPLICATION_ID
SET
application.avg_score = averages.score
UPDATE application
SET `avg_score`=
(SELECT AVG(`score`) as new_score
FROM `rating`
WHERE `ID_APPLICATION` = `application.APPLICATION_ID`)
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
To give a simple analogy, I have a table as follows:
id (PK) | gift_giver_id (FK) | gift_receiver_id (FK) | gift_date
Is it possible to update the table in a single query in such a way that would add a row (i.e. another gift for a person) only if the person has less than 10 gifts so far (i.e. less than 10 rows with the same gift_giver_id)?
The purpose of this would be to limit the table size to 10 gifts per person.
Thanks in advance.
try:
insert into tablename
(gift_giver_id, gift_receiver_id, gift_date)
select GIVER_ID, RECEIVER_ID, DATE from Dual where
(select count(*) from tablename where gift_receiver_id = RECEIVER_ID) < 10
"And would that also be, 'otherwise, update the fields in the oldest row'?"
And would that also be, a rather bloody significant annendum :P
I wouldn't do something that complex in a single query, I'd select first to test for the oldest and then either update or insert accordingly.
Not knowing what language you're working in other than SQL, I'll just stick to pseudocode for non-SQL portions.
SELECT TOP 1 id FROM gifts
WHERE (SELECT COUNT(*) FROM gifts WHERE gift_giver_id = senderidvalue
ORDER BY gift_date ASC) > 9;
{if result.row_count then}
INSERT INTO gifts (gift_giver_id, gift_receiver_id,gift_date)
VALUES val1,val2,val3
{else}
UPDATE gifts SET gift_giver_id = 'val1',
gift_receiver_id = 'val2',gift_date = 'val3'
WHERE {id = result.first_row.id}
The problem with your request is you're trying to find a single query to perform a SELECT as well as either an INSERT or an UPDATE. Someone may well come along and call me out on this to prove me wrong but I think you're asking for the impossible unless you want to get into stored procedures.
I'm no SQL guru but I'm thinking something like the following should work: (assuming a table name of gifts):
INSERT INTO gifts (gift_giver_id, gift_receiver_id,gift_date)
SELECT DISTINCT senderidvalue,receiveridvalue,datevalue FROM gifts
WHERE (SELECT COUNT(*) FROM gifts WHERE gift_giver_id = senderidvalue ) < 10;
[edit] Code formatting doesn't like me :(