MySQL update table add different amount to each row - mysql

MySQL Table:
Key Total
A 20
B 15
C 25
D 30
I have this data:
Key Total
A 5
C 10
D 10
I want to update the table:
by adding each row's total. Note the data is incomplete, so some rows (B) are not going to be updated. So the result should be:
Key Total
A 25
B 15
C 35
D 40
My current plan is running update table set total = total + ? where key = ?. But is there a more efficient way?

If the second set of data were stored in a table then it would have been much easier.
Let's say your first table is named as first_table and your second data set is stored a table named second_table.
Query:
UPDATE first_table AS FT
INNER JOIN second_table AS ST
ON FT.key = ST.key
SET FT.total = FT.total + ST.total;
SEE DEMO

Related

Delete from one table with lookup on another table

As part of a pruning process, I am trying to delete data from a table which is not used anymore. There are search jobs (table Search) which have results (table Res), and people receive the search results (table Abo) via email. For every Abonnement, the last result sent via email is saved in the column "last_result".
What I want to do: For every search job, I want to delete all results which have been sent to the users.
This is a simplified variant of the structure:
http://sqlfiddle.com/#!9/5ef8a3
Abo
---
user search last_result
A 1 100
B 1 200
C 2 50
D 3 100
Res
---
id search
50 1 <---- this should be deleted
300 1
51 2
Search
------
1
2
3
What I tried to do is something like this:
DELETE r
FROM Res r
JOIN Abo a ON r.search = a.search
WHERE
r.id < min(a.last_res)
For every "search" value, I want to delete all results ("Res") which are below the "last_res" field in the "Abo" table.
You need to group MIN(last_res) by search, so the way to do this is to write a subquery that calculates this, then join with that subquery.
DELETE r
FROM Res AS r
JOIN (SELECT search, MIN(last_res) AS min_res
FROM Abo
GROUP BY search) AS a
ON r.search = a.search AND r.id < a.min_res

How to join two tables without primary key or unique key?

I have two tables named LocalVSDB and TemporaryVSDB. Both tables have the same columns:
LocalVSDB: msisdn,activateDate
TemporaryVSDB: msisdn,activateDate
But both tables also have duplicate rows for MSIDSN
I need to join these two tables. My intended result looks like this:
MSISDN LocalActivateDate TemporaryActivateDate Datediff
60103820251 2013-12-14 2013-10-05 70
601111000254 2013-12-14 2013-10-05 70
601111000254 2013-12-18 2013-09-10 80
But, since there are duplicate MSIDSNs, I am getting duplicate rows when I join. For example there are 6 rows for certain MSISDN in each table so when I am joining I am getting total 36 rows for that MSISDN.
I am joining using the following query:
SELECT t.msisdn,t.activateDate AS VSDB_Activate_Date,
l.activateDate AS Local_Activate_Date,
DATEDIFF(D,l.activateDate,t.activateDate) AS date_Diff
FROM temporaryVSDB2 t
INNER JOIN LocalVSDB l ON t.msisdn = l.msisdn
WHERE t.activateDate > l.activateDate
Please help me how can I get 6 rows for 6 MSISDN?
Thanks in advance.
The problem is:
where t.activateDate > l.activateDate
That means one row in table one can join to all six rows in table two. You either need to change this to an = or just get a single row from the second table based on certain criteria.
SELECT m.MSIDN, m.ActiveDate, t.ActiveDate, DATEDIFF(DAY, m.ActiveDate, t.ActiveDate) Duration
FROM LocalVSDB m
OUTER APPLY
(
SELECT TOP 1 d.MSIDN, d.ActiveDate
FROM TemporaryVSDB d
WHERE d.ActiveDate > m.ActiveDate
ORDER BY d.ActiveDate
) t
This would find the nearest partner record and duration (the last record will have a null partner though)
You can use your own query adding group by clause provided msidn and activateDate produce unique row.
SELECT t.msisdn,t.activateDate AS VSDB_Activate_Date,
l.activateDate AS Local_Activate_Date,
DATEDIFF(D,l.activateDate,t.activateDate) AS date_Diff
FROM temporaryVSDB2 t INNER JOIN LocalVSDB l ON t.msisdn = l.msisdn
WHERE t.activateDate > l.activateDate
group by t.msisdn, t.activateDate

mysql select statement. why does it return 6 of the same record

When I make this sql statement I get 6 of the same record returned. So if I expect to get 2 records returned, I get six of each record back so that is 12 in total.
SELECT
ce2.*
FROM customerentry ce, customerentrytrace cet, customerentry ce2
WHERE ce.accountid = 1
AND ce.companyid = 1
AND ce.accountid=cet.accountid
AND ce.accountid=ce2.accountid
AND ce.companyid=cet.companyid
AND ce.companyid=ce2.companyid
AND cet.documentno = '2012Faktura1'
AND cet.documenttype = 1
AND ce2.documentno = cet.offsetdocumentno
AND ce2.documenttype = cet.offsetdocumenttype
ORDER BY created;
I know that I can solve it by adding distinct, but I would like to know why I get 6 of the same record returned. Anyone who can help me?
Since we have no idea about your table structure probably there are some columns that are related 1 to n items and you haven't handled them in the WHERE section of your query.
As an extra measure you can focus on your data needs and add a GROUP BY section before your ORDER section.
You are using an INNER JOIN, so for example there are two entries in table cet matching your where clause for combining table ce and cet, giving you 2 entries/entry of table ce.
Thinking this further you can see that if there are 3 entries in table ce2 matching the where clause for combining table cet and ce2 you get 3 entries/entry of table cet.
Which makes 6 entries per entry of table ce in total, giving you 12 entries in total even if you have only 2 entries in table ce.
So think again about what join could be the right for your desired solution.
Here a link for some more explanation: Short explanation of joins
Problem might be because you have not properly joined tables. Please read about JOIN
SELECT ce2.*
FROM customerentry ce INNER JOIN customerentrytrace cet ON ce.accountid=cet.accountid AND ce.companyid=cet.companyid,
INNER JOIN customerentry ce2 ON ce.accountid=ce2.accountid AND ce.companyid=ce2.companyid AND ce2.documentno = cet.offsetdocumentno AND ce2.documenttype = cet.offsetdocumenttype
WHERE ce.accountid = 1
AND ce.companyid = 1
AND cet.documentno = '2012Faktura1'
AND cet.documenttype = 1
ORDER BY created;

How to perform a complex range query in MySQL

I have a table (Table A) with a field of integers (Field B). For each row of Table A, I would like to construct a range of +/- 100 surrounding the integer value of Field B then find all values from Field B that are within these ranges. The query needs to be performed for all values in Field B. The query needs to return each row that is within each row range. Here is an example of what I am trying to do:
Table A
_______
A 1000
B 3000
C 5000
D 1090
Using the above Table A, the query would first find the ranges (+/- 100) for all integers in Field B.
900 - 1100
2900 - 3100
4900 - 5100
990 - 1190
The query would then iterate through these ranges and return rows from Table A that fall within the generated ranges. Using the above example, the query would return:
A 1000
A 1000
B 3000
C 5000
D 1090
D 1090
A and D are returned twice because it they fall within their own ranges. How can I construct a query that will return each row that falls between the range of each row? Thanks in advance for the help.
SELECT t2.*
FROM tableA AS t1
INNER JOIN tableA AS t2 ON t2.fieldB >= (t1.fieldB - 100) AND t2.fieldB <= (t1.fieldB + 100)
Shouldn't A also be shown twice, since it's also in D's range? (that's the case with above query - if incorrect, please elaborate why ^^)
Start with your inner-most pre-qualifier of every Table A record... Then re-join to table A again. I've added the qualifying Group ranges low and hi to show the qualifier basis you were looking for... In addition to D showing up twice, A should show up twice too as it qualifies the "D"s range too.
select
a2.ShowLetter,
a2.FieldB,
GrpRanges.RangeLow,
GrpRanges.RangeHi
from
( select distinct
a1.FieldB - 100 as RangeLow,
a1.FieldB + 100 as RangeHi
from
TableA a1 ) GrpRanges
JOIN TableA a2
on a2.FieldB between GrpRanges.RangeLow and GrpRanges.RangeHi
order by
a2.ShowLetter

Obtain running frequency distribution from previous N rows of MySQL database

I have a MySQL database where one column contains status codes. The column is of type int and the values will only ever be 100,200,300,400. It looks like below; other columns removed for clarity.
id | status
----------------
1 300
2 100
3 100
4 200
5 300
6 300
7 100
8 400
9 200
10 300
11 100
12 400
13 400
14 400
15 300
16 300
The id field is auto-generated and will always be sequential. I want to have a third column displaying a comma-separated string of the frequency distribution of the status codes of the previous 10 rows. It should look like this.
id | status | freq
-----------------------------------
1 300
2 100
3 100
4 200
5 200
6 300
7 100
8 400
9 300
10 300
11 100 300,100,200,400 -- from rows 1-10
12 400 100,300,200,400 -- from rows 2-11
13 400 100,300,200,400 -- from rows 3-12
14 400 300,400,100,200 -- from rows 4-13
15 300 400,300,100,200 -- from rows 5-14
16 300 300,400,100 -- from rows 6-15
I want the most frequent code listed first. And where two status codes have the same frequency it doesn't matter to me which is listed first but I did list the smaller code before the larger in the example. Lastly, where a code doesn't appear at all in the previous ten rows, it shouldn't be listed in the freq column either.
And to be very clear the row number that the frequency string appears on does NOT take into account the status code of that row; it's only the previous rows.
So what have I done? I'm pretty green with SQL. I'm a programmer and I find this SQL language a tad odd to get used to. I managed the following self-join select statement.
select *, avg(b.status) freq
from sample a
join sample b
on (b.id < a.id) and (b.id > a.id - 11)
where a.id > 10
group by a.id;
Using the aggregate function avg, I can at least demonstrate the concept. The derived table b provides the correct rows to the avg function but I just can't figure out the multi-step process of counting and grouping rows from b to get a frequency distribution and then collapse the frequency rows into a single string value.
Also I've tried using standard stored functions and procedures in place of the built-in aggregate functions, but it seems the b derived table is out of scope or something. I can't seem to access it. And from what I understand writing a custom aggregate function is not possible for me as it seems to require developing in C, something I'm not trained for.
Here's sql to load up the sample.
create table sample (
id int NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
status int
);
insert into sample(status) values(300),(100),(100),(200),(200),(300)
,(100),(400),(300),(300),(100),(400),(400),(400),(300),(300),(300)
,(100),(400),(100),(100),(200),(500),(300),(100),(400),(200),(100)
,(500),(300);
The sample has 30 rows of data to work with. I know it's a long question, but I just wanted to be as detailed as I could be. I've worked on this for a few days now and would really like to get it done.
Thanks for your help.
The only way I know of to do what you're asking is to use a BEFORE INSERT trigger. It has to be BEFORE INSERT because you want to update a value in the row being inserted, which can only be done in a BEFORE trigger. Unfortunately, that also means it won't have been assigned an ID yet, so hopefully it's safe to assume that at the time a new record is inserted, the last 10 records in the table are the ones you're interested in. Your trigger will need to get the values of the last 10 ID's and use the GROUP_CONCAT function to join them into a single string, ordered by the COUNT. I've been using SQL Server mostly and I don't have access to a MySQL server at the moment to test this, but hopefully my syntax will be close enough to at least get you moving in the right direction:
create trigger sample_trigger BEFORE INSERT ON sample
FOR EACH ROW
BEGIN
DECLARE _freq varchar(50);
SELECT GROUP_CONCAT(tbl.status ORDER BY tbl.Occurrences) INTO _freq
FROM (SELECT status, COUNT(*) AS Occurrences, 1 AS grp FROM sample ORDER BY id DESC LIMIT 10) AS tbl
GROUP BY tbl.grp
SET new.freq = _freq;
END
SELECT id, GROUP_CONCAT(status ORDER BY freq desc) FROM
(SELECT a.id as id, b.status, COUNT(*) as freq
FROM
sample a
JOIN
sample b ON (b.id < a.id) AND (b.id > a.id - 11)
WHERE
a.id > 10
GROUP BY a.id, b.status) AS sub
GROUP BY id;
SQL Fiddle