swap "two" column values in two different rows using mysql - mysql

I have two rows each of which contain a week a day and an event. An auto increment primary key is used to distinguish the rows.
Here is an example:
ID Week Day event
-------------------------------
1 | 1 | 2 | house keeping
2 | 2 | 3 | house viewing
What i want to do is swap the week and day of the two rows specified so that it looks like this:
ID Week Day event
-------------------------------
1 | 2 | 3 | house keeping
2 | 1 | 2 | house viewing
But the Id must remain the same
Ive been reading through other peoples posts and found this solution which uses temporary variables to swap only one columns values from each row.
UPDATE my_table SET a=#tmp:=a, a=b, b=#tmp;
Could anyone help me swap two columns instead of just the one?
thanks

I assume you have just 2 rows in your table.
If not, you need to modify slightly the JOIN conditions.
Here is one possible approach.
CREATE TEMPORARY TABLE T(ID int, Week int, Day int)
INSERT INTO T(ID, Week, Day)
SELECT ID, Week, Day from TableName;
UPDATE TableName t1
JOIN T t2 on t1.ID <> t2.ID
SET
t1.Week = t2.Week,
t1.Day = t2.Day;
DROP TEMPORARY TABLE T;
And here is a better one.
UPDATE
tablename AS t1
JOIN tablename AS t2 ON
( t1.id <> t2.id )
SET
t1.week = t2.week,
t2.week = t1.week,
t1.day = t2.day,
t2.day = t1.day;

Related

Remove continuous duplicated values with different IDs in MySQL

I know there is a ton of same questions about finding and removing duplicate values in mySQL but my question is a bit different:
I have a table with columns as ID, Timestamp and price. A script scrapes data from another webpage and saves it in the database every 10 seconds. Sometimes data ends up like this:
| id | timestamp | price |
|----|-----------|-------|
| 1 | 12:13 | 100 |
| 2 | 12:14 | 120 |
| 3 | 12:15 | 100 |
| 4 | 12:16 | 100 |
| 5 | 12:17 | 110 |
As you see there are 3 duplicated values and removing the price with ID = 4 will shrink the table without damaging data integrity. I need to remove continuous duplicated records except the first one (which has the lowest ID or Timestamp).
Is there a sufficient way to do it? (there is about a million records)
I edited my scraping script so it checks for duplicated price before adding it but I need to shrink and maintain my old data.
Since MySQL 8.0 you can use window function LAG() in next way:
delete tbl.* from tbl
join (
-- use lag(price) for get value from previous row
select id, lag(price) over (order by id) price from tbl
) l
-- join rows with same previous price witch will be deleted
on tbl.id = l.id and tbl.price = l.price;
fiddle
I am just grouping based on price and filtering only one record per group.The lowest id gets displayed.Hope the below helps.
select id,timestamp,price from yourTable group by price having count(price)>0;
My query is based on #Tim Biegeleisen one.
-- delete records
DELETE
FROM yourTable t1
-- where exists an older one with the same price
WHERE EXISTS (SELECT 1
FROM yourTable t2
WHERE t2.price = t1.price
AND t2.id < t1.id
-- but does not exists any between this and the older one
AND NOT EXISTS (SELECT 1
FROM yourTable t3
WHERE t1.price <> t3.price
AND t3.id > t2.id
AND t3 < t1.id));
It deletes records where exists an older one with same price but does not exists any different between
It could be checked by timestamp column if id column is not numeric and ascending.

How to Delete Duplicate Rows Based on 3 Column Values and Length in MySQL

I wanted some help in regards to understanding how I can delete duplicate records from my database table. I have a table of 1 million records which has been collected over a 2 year period hence there is a number of records that need to be deleted as they have been added numerous times into the database.
The following is a query that I wrote based on the three columns that I am matching for duplicates, taking a count and I have also added a length of one of the columns as this will determine whether I delete all the records or just the duplicates.
SELECT
Ref_No,
End_Date,
Filename,
count(*) as cnt,
length(Ref_No)
FROM
master_table
GROUP BY
Ref_No,
End_Date,
Filename,
length(Ref_No)
HAVING
COUNT(*) > 1
;
This then gives me an output like the following:
Ref_No | End_Date | Filename | cnt | length(Ref_No)
05011384 | 2018-07-01 | File1 | 2 | 8
1234 | 2018-12-31 File2 | 11 | 4
1000002975625 | 2018-12-31 | File3 | 13
123456789123456789 | 2019-02-06 | File3 | 18
Now I have a list of rules to follow based on the length column and this will determine whether I leave the records as they are with the duplicates, delete the duplicates or delete all the records and this is where I am stuck.
My rules are the following:
If length is between 0 and 4 - Keep all records with duplicates
If length is between 5 and 10 - Delete Duplicates, keep 1 record
If length equals 13 - Delete Duplicates, keep 1 record
If length is 11, 12, 14-30 - Delete all records
I would really appreciate if some could advice on how I go about completing this task.
Thanks.
I have managed to create a temporary table in which I add a unique id. The only thing is that I am running the query twice with the length part changed for my requirements.
INSERT INTO UniqueIDs
(
SELECT
T1.ID
FROM
master_table T1
LEFT JOIN
master_table T2
ON
(
T1.Ref_No = T2.Ref_No
AND
T1.End_Date = T2.End_Date
AND
T1.Filename = T2.Filename
AND
T1.ID > T2.ID
)
WHERE T2.ID IS NULL
AND
LENGTH(T1.Ref_No) BETWEEN 5 AND 10
)
;
I then just run the following delete to keep the unique ids in the table and remove the rest.
DELETE FROM master_table WHERE id NOT IN (SELECT ID FROM UniqueIDs);
That's it.

Update MySQL Table from Subquery/Joined Same Table

I've seen many questions along this issue, but can't get this to work.
I want to UPDATE multiple columns in a table (but will start with one) based upon a calculated value from the same table.
It is a list of transactions per customer, per month.
TransID | Cust | Month | Value | PastValue | FutureValue
1 | 1 | 2018-01-01 | 45 |
2 | 1 | 2018-02-01 | 0 |
3 | 1 | 2018-03-01 | 35 |
4 | 1 | 2018-04-01 | 80 |
.
UPDATE tbl_transaction a
SET PrevMnthValue =
(SELECT COUNT(TransactionID) FROM tbl_transaction b WHERE b.Cust=a.Cust AND b.Month<a.Month)
But we get the dreaded 'Can't update a table using a where with a subquery of the same table).
I've tried to nest the subquery as this has been touted as a workaround:
UPDATE tbl_transactions a
SET
PastValue =
(
SELECT CNT FROM
(
SELECT
COUNT(TransactionID) AS CNT
FROM tbl_transactions b
WHERE
b.CustomerRef=a.CustomerRef AND b.Month<a.Month
) x
),
FutureValue =
(
SELECT CNT FROM
(
SELECT
COUNT(TransactionID) AS CNT
FROM tbl_transactions b
WHERE
b.CustomerRef=a.CustomerRef AND b.Month>a.Month
) x
)
But I get an UNKNOWN a.CustomerRef in WHERE clause. Where am I going wrong?
You can't update and read from one table at the same time.
MySQL documentation tell about it
You cannot update a table and select from the same table in a subquery.
At first you must select necessary data and save them to somewhere, for example to temporary table
CREATE TEMPORARY TABLE IF NOT EXISTS `temp` AS (
SELECT
COUNT(`TransactionID`) AS CNT,
`CustomerRef`,
`Month`
FROM `tbl_transactions`
GROUP BY `Custom,erRef`, `Month`
);
After it, you can use JOIN statement for update table
UPDATE `tbl_transactions` RIGTH
JOIN `temp` ON `temp`.`CustomerRef` = `tbl_transactions`.`CustomerRef`
AND `temp`.`Month` < `tbl_transactions`.`Month`
SET `tbl_transactions`.`PastValue` = `temp`.`cnt`
UPDATED: if you want to update several columns by different condition you can combine temporary table, UPDATE + RIGHT JOIN and CASE statement. For example:
UPDATE `tbl_transactions`
RIGTH JOIN `temp` ON `temp`.`CustomerRef` = `tbl_transactions`.`CustomerRef`
SET `tbl_transactions`.`PastValue` = CASE
WHEN `temp`.`Month` < `tbl_transactions`.`Month` THEN `temp`.`cnt`
ELSE `tbl_transactions`.`PastValue`
END,
`tbl_transactions`.`FutureValue` = CASE
WHEN `temp`.`Month` > `tbl_transactions`.`Month` THEN `temp`.`cnt`
ELSE `tbl_transactions`.`FutureValue`
END
You can try below
UPDATE tbl_transactions a
Join
( SELECT CustomerRef,COUNT(TransactionID) AS CNT FROM tbl_transactions b
group by CustomerRef)x
SET PastValue = CNT
WHERE x.CustomerRef=a.CustomerRef AND x.Month<a.Month

Sql: How to connect tables by just using the ID

so I have this problem: I have 3 different tables that need to be connected to one another. It's rather complex and my english skills aren't that good. Let me show you:
T1 T2 T3
ID_T1 |Name | |ID_T1|ID_T2| |ID_T2|Spent|
1 |James| | 1| 4| | 4 | 200 |
2 |Mike | | 2| 5| | 5 | 300 |
3 |Alex | | 3| 6| | 6 | 400 |
Basically I want to connect in Table T3 the amount spent with the names in T1 by using the ID I have in T2.
So I should "read" that James spent 200 dollars, Mike spent 300 and Alex 400.
Another thing to know is that the IDs are generated automatically, so I'm not supposed to be able to see them.
Do I have to pay attention on something when I create my Tables, or do I have to focus more on the INSERT INTO command (I work with Oracle sql Developer)?
Thank you very much! :)
#Burkinaboy might wanna try this
SELECT
T1.Name,
T3.SPent
FROM T1 t0 RIGHT JOIN T2 t1 ON T1.ID_T1 = T2.ID_T1 RIGHT JOIN T3 t2 ON T3.ID_T2=T2.ID_T2
This should give you an expected output.
Select a.Name, c.spend
From T1 a, T2 b, T3 c
Where a.Id_T1 = b.Id_T1
AND b.Id_T2 = c.Id_T2
Regarding your second question, you should know the relation between the tables meaning how they are connected.
Updating the answer as per your comments . Assuming that amount spend column you are going to hard code..
Select b.Id_T2 , "300"
From T1 a ,T2 b
Where a.Id_T1 = b.Id_T1
But in above case , all the values for column spend would be 300.
SELECT T1.name, T3.spent FROM T1, T3 WHERE T1.ID_T1=T2.ID_T1 AND T2.ID_T2=T3.ID_T2
select t1.name as name, t3.spent as spent
from t1
join t2 on t2.id_t1 = t1.id_t1
join t3 on t3.id_t2 = t2.id_t2
Use join instead of putting all tables in FROM clause.
Assuming you are inserting every spending in T3 table with a new ID, you can join the three tables and do a group by on it:
select
T1.ID_T1 ID,
T1.NAME,
SUM(T3.SPENT) SPENT
from T1 inner join T2 on T1.ID_T1 = T2.ID_T1
inner join T3 on T2.ID_T2 = T3.ID_T2
GROUP BY T1.ID_T1, T1.NAME;
As of now, It's not clear which database you're using exactly. (I assumed that it's Oracle as you're using Oracle SQL Developer. Similar solution should be available in other DBMS as well in case it's not.).
First, Let's create the tables:
create table t1 (
ID_T1 integer primary key,
Name varchar2(100)
);
create table t2 (
ID_T1 integer,
ID_T2 integer,
constraint pk primary key(ID_T1,ID_T2)
);
create table t3 (
ID_T2 integer primary key,
spent number(10,2)
);
Then create two sequences that'll be used while inserting:
create sequence t1_seq start with 1 increment by 1;
create sequence t2_seq start with 1 increment by 1;
Then a view which will display spendings per person:
create or replace view spendings
as
select
T1.ID_T1 ID,
T1.NAME,
SUM(T3.SPENT) SPENT
from T1 inner join T2 on T1.ID_T1 = T2.ID_T1
inner join T3 on T2.ID_T2 = T3.ID_T2
GROUP BY T1.ID_T1, T1.NAME;
Then create an INSTEAD OF trigger on the above view to make the proper inserts in underlying tables:
create or replace trigger spendings_trig
instead of INSERT or UPDATE or DELETE on SPENDINGS
for each row
declare
v_ID_T1 integer;
v_ID_T2 integer;
v_name t1.name%type;
begin
if not inserting then
raise_application_error(-20001,'Not supported');
end if;
begin
if :new.ID is null then
v_ID_T1 := T1_SEQ.NEXTVAL;
insert into T1 (ID_T1, NAME) values (v_ID_T1, :new.NAME);
end if;
select ID_T1,NAME into v_ID_T1,v_name from T1
where ID_T1 = :new.ID;
if :new.name is not null and v_name <> :new.name then
raise_application_error(-20002,'Incorrect name entered');
end if;
exception
when no_data_found then
v_ID_T1 := :new.ID;
insert into T1 (ID_T1, NAME) values (v_ID_T1, :new.NAME);
end;
v_ID_T2 := T2_SEQ.NEXTVAL;
insert into T2 values (v_ID_T1, v_ID_T2);
insert into T3 values (v_ID_T2, :new.spent);
end;
/
Now if you insert into the view like this (assuming there is no data in any of the tables):
insert into spendings (name, spent) values ('James',100);
T1 will get a row
ID_T1 NAME
------------
1 James
T2 will get a row
ID_T1 ID_T2
------------
1 1
T3 will get a row
ID_T2 SPENT
-------------
1 100
Now if you do:
insert into spendings (id,spent) values (1,100);
T1 will not be inserted as there is already row present for ID = 1
T2 will get a row:
ID_T1 ID_T2
------------
1 1
1 2
T3 will get a row
ID_T2 SPENT
-------------
1 100
2 100
And
select * from spendings;
will show:
ID NAME SPENT
------------------
1 James 200

Differences between timestamps in MySQL Table

I have a question regarding interval calculation with timestamps:
Let´s assume I have a table with IDs and timestamps:
Table tab1
tab1.id deviceid timestamp
------- -------- ----------
1 15 2013-01-01 14:57:54
2 15 2013-01-01 14:58:09
3 23 2013-01-01 14:58:10
4 15 2013-01-01 14:58:30
What I want to do is to check if the intervals between the entries are inside or outside a specific value. Let´s assume 15 sec is the value:
The interval between 1 and 2 is OK, between 2 and 3 not OK.
Any hints how to solve this without using a temporary table and stored procedure etc.?
Thanks in advance and kind regards
solick
EDIT: Updated the table. There are timestamps from other devices in between the entries.
You can start with this. This will join the table against itself and calculate the +1 id to calculate the difference in seconds.
SELECT a.id,
TIME_TO_SEC(TIMEDIFF(b.timestamp, a.timestamp)) AS time_diff
FROM tab1 a
LEFT JOIN tab1 b ON b.id = a.id + 1
WHERE b.id IS NOT NULL
Result
| ID | TIME_DIFF |
------------------
| 1 | 15 |
| 2 | 21 |
SQL Fiddle
Solution:
njk give me the right hint, but at the end it was necessary to use a stored procedure and temoprary table:
Important here: Temoprary tables cannot be used more than once within a query, therefore i needed to copy the temporary table:
BEGIN
DROP TEMPORARY TABLE IF EXISTS tmptable1;
DROP TEMPORARY TABLE IF EXISTS tmptable2;
CREATE TEMPORARY TABLE tmptable1 (tmpid INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY)
SELECT * FROM tbl_source WHERE tbl_source.Id = did;
CREATE TEMPORARY TABLE tmptable2 AS (SELECT * FROM tmptable1);
SELECT a.tmpid, a.timestamp, TIME_TO_SEC(TIMEDIFF(b.timestamp, a.timestamp)) AS time_diff
FROM tmptable1 a LEFT JOIN tmptable2 b
ON b.tmpid = a.tmpid +1
WHERE b.tmpid IS NOT NULL;
DROP TEMPORARY TABLE IF EXISTS tmptable1;
DROP TEMPORARY TABLE IF EXISTS tmptable2;
END