MYSQL - Selecting data from second row in a large table - mysql

I have an external 3rd party program export the database to mysql in real time, and I want to show data for reporting. So, I can't change the structure, because it's being sync in real time.
The table structure is something like this
ID | Date | Transaction
-----------------------------
12 | 2012-11-01 | 200
12 | 2012-11-02 | 250
12 | 2012-11-03 | 150
12 | 2012-11-04 | 1000
12 | 2012-11-05 | 225
....
13 | 2012-11-01 | 175
13 | 2012-11-02 | 20
13 | 2012-11-03 | 50
13 | 2012-11-04 | 100
13 | 2012-11-05 | 180
13 | 2012-11-06 | 195
The data are very large and keep getting bigger each day.
What I want to do is to build a report (view table) based on something like this:
ID | Date | Transaction | Prev Day Transaction
----------------------------------------------------
12 | 2012-11-01 | 200 | 0
12 | 2012-11-02 | 250 | 200
12 | 2012-11-03 | 150 | 250
12 | 2012-11-04 | 1000 | 150
12 | 2012-11-05 | 225 | 1000
....
13 | 2012-11-01 | 175 | 0
13 | 2012-11-02 | 20 | 175
13 | 2012-11-03 | 50 | 20
13 | 2012-11-04 | 100 | 50
13 | 2012-11-05 | 180 | 100
13 | 2012-11-06 | 195 | 180
I just can't get the fast select statement. Currently the original data is already 283,120 rows. And it will grow like 500 rows daily.
I've tried something like:
SELECT *, (SELECT transaction FROM table as t2 WHERE t1.id=t2.id
AND t1.date>t2.date ORDER BY t2.date DESC LIMIT 0,1)
FROM table AS t1
It's working, but the select statement is very slow. Most of the time, it's getting cut of in the middle of the operation.
What I need help is a very fast sql statement, which later on I could use to build the view table.

See this link: http://sqlfiddle.com/#!2/54a5e/12
select t.id,t.cDate,t.cTrans
,(case when #pID=t.id then #pTran else 0 end) as preT
,(#pID :=t.id) as `tID`,(#pTran := t.cTrans) as `tTrans`
from tb_test_1 as t,(select #pID = 0, #pTran = 0) as t2
order by id,cDate;
tID and tTrans column must be retained, and cannot display on page.
Please forgive me as I only know a little english!

Try this query -
SELECT t1.*, COALESCE(t2.transaction, 0) Prev_Day_Transaction FROM trans t1
LEFT JOIN (SELECT * FROM trans ORDER BY id, date DESC) t2
ON t1.id = t2.id AND t1.date > t2.date
GROUP BY t1.id, t1.date;
+------+------------+-------------+----------------------+
| id | date | transaction | Prev_Day_Transaction |
+------+------------+-------------+----------------------+
| 12 | 2012-11-01 | 200 | 0 |
| 12 | 2012-11-02 | 250 | 200 |
| 12 | 2012-11-03 | 150 | 250 |
| 12 | 2012-11-04 | 1000 | 150 |
| 12 | 2012-11-05 | 225 | 1000 |
| 13 | 2012-11-01 | 175 | 0 |
| 13 | 2012-11-02 | 20 | 175 |
| 13 | 2012-11-03 | 50 | 20 |
| 13 | 2012-11-04 | 100 | 50 |
| 13 | 2012-11-05 | 180 | 100 |
| 13 | 2012-11-06 | 195 | 180 |
+------+------------+-------------+----------------------+
Add composite index (id, date) to the table.
===========================
ALTER TABLE mt4_daily
ADD INDEX IX_mt4_daily_DATE (DATE);
ALTER TABLE mt4_daily
ADD INDEX IX_mt4_daily (ID, DATE);

Divide the table into few pars through select statements and join them using UNION Set operator. As all set operators are parallel operation you will get the data very quickly. You can divide the data by using the Unique numeric column in your table. e.g.
select * from tbl_x where col1%3=0 union
select * from tbl_x where col1%3=1 union
select * from tbl_x where col1%3=2 ...
The above sql query divides the data and fetches in parallel way

I would try to write the query like this:
SELECT
tbl.ID,
tbl.Date,
tbl.Transaction,
COALESCE(tbl1.Transaction,0) as PrevDay
FROM
tbl left join tbl tbl1
on tbl.ID = tbl1.ID
and tbl.Date = tbl1.Date + INTERVAL 1 DAY
(this will work only if you make sure that the table contains all days, if you miss one day, the next day will always show PrevDay as 0, i am not sure if this is what you need).
EDIT: i would try this solution that works even if some days are missing:
SELECT
tbl.id,
tbl.date,
tbl.Transaction,
COALESCE(tbl1.Transaction,0) as PrevDay
FROM
(SELECT tbl.id, tbl.date as d1, max(tbl1.ddate) as d2
FROM tbl LEFT JOIN tbl tbl1
ON tbl.id = tbl1.id and tbl.date>tbl1.date
GROUP BY tbl.id, tbl.date) t
LEFT JOIN tbl on tbl.id = t.id and DATE(tbl.ddate) = DATE(t.d1)
LEFT JOIN tbl tbl1 ON tbl1.id = t.id and DATE(tbl1.date) = DATE(t.d2)

Related

Merge two tables with same structure and numers sum

Hello i have two tables with same structure and now I want merge it.
Here is structure:
Terms:
steamid - that goes without saying
regcas - keep only a smaller value
VIP - sum
FunVIP - ignore when duplicate
Days - sum
KilledCT - sum
WinPP - sum
LastT - sum
cas - sum
lastnick - ignore when duplicate
lastlog - ignore when duplicate
ct_cas - sum
simon_cas - sum
Example when duplicate:
row from main table
steamid | regcas | VIP | FunVIP | Days | KilledCT | WinPP | LastT | lastnick | lastlog | ct_cas | simon_cas
------------------------------------------------------------------------------------------------------------------------------
76561198040874389 | 1546639030 | 1 | 0 | 125 | 1000 | 20 | 50 | Bomber | 1546639037 | 64 | 50
row from second table
steamid | regcas | VIP | FunVIP | Days | KilledCT | WinPP | LastT | lastnick | lastlog | ct_cas | simon_cas
------------------------------------------------------------------------------------------------------------------------------
76561198040874389 | 1553888234 | 1 | 5 | 100 | 1555 | 40 | 20 | Lucker | 1549387793 | 10 | 1
Result
steamid | regcas | VIP | FunVIP | Days | KilledCT | WinPP | LastT | lastnick | lastlog | ct_cas | simon_cas
------------------------------------------------------------------------------------------------------------------------------
76561198040874389 | 1546639030 | 2 | 0 | 225 | 2555 | 60 | 70 | Bomber | 1546639037 | 74 | 51
I absolutely don't know how to compose a complex SQL statement and I need help.
You seem to want union all and group by. I have no idea what "ignore with duplicate" is supposed to mean, but min() seems close enough. So:
select steamid, min(regcas) as regcas, sum(vip) as vip),
min(FunVIP) as FunVIP,
sum(Days) as days, sum(KilledCT) as KilledCT, sum(WinPP) as WinPP,
sum(LastT) as LastT, sum(cas) as cas,
min(lastnick) as lastnick,
min(lastlog) as lastlog,
sum(ct_cas) as ct_cas, sum(simon_cas) as simon_cas
from ((select t1.* from table1 t1) union all
(select t2.* from table2 t2)
) t2
group by steamid;
To merge two tables.Can use join tables command.
select*from natural join ;
OR [1]
select*from, where column.table1=column.table2;

How to get next row value from Mysql Query

I want to get (select) the next row value from MySql query using SQLYog, with
the same id_voyage_order?
Example :
id_timesheet | id_voyage_order | Duration
----------------------------------------
1 | 106 | 0.00
2 | 106 | 24.00
3 | 210 | 12.00
4 | 106 | 12.00
5 | 210 | 24.00
i want to select it and make in View file Yii based on the same id_voyage order become like this and set the Duration of the last record become 0 :
id_timesheet | id_voyage_order | Duration
1. | 106 | 24.00
2. | 106 | 12.00
4. | 106 | 0.00
For instance:
SELECT a.id_timesheet
, a.id_voyage_order
, COALESCE(b.duration,0) duration
FROM my_table a
LEFT
JOIN
( SELECT x.*
, MAX(y.id_timesheet) previous
FROM my_table x
JOIN my_table y
ON y.id_voyage_order = x.id_voyage_order
AND y.id_timesheet < x.id_timesheet
GROUP
BY x.id_timesheet
) b
ON b.previous = a.id_timesheet
WHERE a.id_voyage_order = 106;

Merging info from two queries into a single table

Query 1:
SELECT num_requerimiento, asunto
FROM masivos_texto INNER JOIN envios_masivos
ON id_masivos=id_envio;
Result 1:
+---------------------+---------------------+
| num_requerimiento | asunto |
|---------------------+----------------------
| 1800 | inscripcion |
|---------------------+---------------------+
| 1801 | seguimiento |
+---------------------+---------------------+
Query 2:
SELECT id_envio, estatus, count(estatus)
FROM acuses_recibo
WHERE id_envio IN (SELECT id_masivos FROM cati_atencion.masivos_texto WHERE fecha >= '2014-01-01' AND fecha <= '2015-06-16')
GROUP BY id_envio, estatus;
Result 2:
+---------------------+---------------------+----------------------+
| id_envio | estatus | count(estatus) |
|---------------------+--------------------------------------------+
| 84 | 0 | 4031 |
|---------------------+---------------------+----------------------+
| 84 | 1 | 632 |
+---------------------+---------------------+----------------------+
| 85 | 0 | 35635 |
+---------------------+---------------------+----------------------+
| 85 | 1 | 3711 |
+---------------------+---------------------+----------------------+
Desired Result:
+---------------------+-----------------+------------+------------+-------------------+
| num_requerimiento | asunto | id_envio | estatus | count(estatus) |
|---------------------+-----------------+------------+------------+-------------------+
| 1800 | inscripcion | 84 | 0 | 4031 |
|---------------------+-----------------+------------+------------+-------------------+
| 1800 | inscripcion | 84 | 1 | 632 |
+---------------------+-----------------+------------+------------+-------------------+
| 1801 | seguimiento | 85 | 0 | 635 |
+---------------------+-----------------+------------+------------+-------------------+
| 1801 | seguimiento | 85 | 1 | 711 |
+---------------------+-----------------+------------+------------+-------------------+
in the Desired Result the id_envio/id_masivos corresponding to num_requerimiento 1800 is 84,
and the id_envio/id_masivos corresponding to num_requerimiento 1801 is 85,
and estatus in the 2nd table cant take up to three values, than i.a. for your assistance.
UNION doesn´t work, it gives me the 1st table followed by the 2nd, and only if the selects are of the same number of columns.
To do this with SQL, you will need a table relating your masivos_texto and acuses_recibo tables. I suggest you create a table. You could call it req_id or anything suitable. This is often called a JOIN table. It will have this content
num_requerimiento id_envio
1800 84
1801 85
Then you'll be able to join your first and second queries together appropriately.
It's not possible to write your query for you without knowing the rows of your tables.
Solved!! I needed to use aliases to each SELECT, as adding an alias to each select level, like this:
SELECT result1.num_requerimiento, result1.asunto, result1.id_masivos, result2.estatus, result2.conteo
FROM
(SELECT C.num_requerimiento, B.asunto, B.id_masivos
FROM masivos_texto B INNER JOIN envios_masivos C
ON B.id_masivos=C.id_envio) as result1
INNER JOIN
(SELECT A.id_envio, A.estatus, count(estatus) as conteo
from acuses_recibo A
WHERE A.id_envio IN (SELECT B.id_masivos FROM masivos_texto B where B.fecha >= '2014-01-01' AND B.fecha <= '2015-06-16')
GROUP BY A.id_envio, A.estatus) as result2
ON result1.id_masivos=result2.id_envio;
and that generates the 3rd table needed. Hope it helps someone in the future.

MySql selecting greatest difference between 2 rows within the past day

I have a table that sort of looks like this
id | name | c1 | c2 | c3 | c4 | time
-------------------------------------------------
1 | miley | 23 | 11 | 21 | 18 | 2013-01-13 20:26:25
2 | john | 31 | 29 | 23 | 27 | 2013-01-14 20:26:25
3 | steve | 44 | 31 | 33 | 35 | 2013-01-14 20:26:25
4 | miley | 34 | 44 | 47 | 48 | 2013-01-15 08:26:25
5 | john | 27 | 53 | 49 | 52 | 2013-01-15 08:26:25
6 | steve | 27 | 62 | 50 | 64 | 2013-01-16 08:26:25
7 | miley | 44 | 54 | 57 | 87 | 2013-01-16 20:26:25
8 | john | 37 | 93 | 59 | 62 | 2013-01-17 20:26:25
9 | steve | 85 | 71 | 87 | 74 | 2013-01-17 20:26:25
...etc
*note: this is a random table I made up to just give you an idea of what my table looks like
I need to grab the name for who had the greatest change in a specific column over the course of a specific date range. I've tried a bunch of different queries by can't get one to work. I think my closest solution is something like...
SELECT table1.name, MAX(table1.c1-h.c1) as maxDiff
FROM table_a as table1
LEFT JOIN table_a as table2
ON table2.name=table1.name AND table1.c1>table2.c1
WHERE table2.c1 IS NOT NULL
What am I doing wrong? To be clear, I want to be able to select a range of dates then determine who has the biggest difference for that date range in a determined column. Also note that the data only increments over time, so the first capture of any day will always be <= the last capture of the day for that person.
It sounds like you will be needing a nested query. First, a query of each person on their own measurements within the date range, then order it by the biggest and take the top 1... something like this may work for you...
select
PreGroupByName.`Name`,
PreGroupByName.MaxC1 - PreGroupByName.MinC1 as MaxSpread
from
( select
t1.`Name`,
min( t1.c1 ) as MinC1,
max( t1.c1 ) as MaxC1
from
table_a t1
where
t1.`time` between '2013-01-01' and '2013-01-17' -- or whatever date/time range
group by
t1.`Name` ) as PreGroupByName
order by
MaxSpread DESC
limit 1
SELECT
`id`,`name`
,MAX(`c1`)-MIN(`c1`) AS `diff_c1`
-- ,MAX(`c2`)-MIN(`c2`) AS `diff_c2`
-- ,MAX(`c3`)-MIN(`c3`) AS `diff_c3`
-- ,MAX(`c4`)-MIN(`c4`) AS `diff_c4`
FROM `the_table`
WHERE `time` BETWEEN '2013-01-13 20:26:25' AND '2013-01-17 20:26:25'
GROUP BY `name`
ORDER BY `diff_c1` DESC -- whichever you want to evaluate
LIMIT 1

How to determine daily accumlated values in mysql for each sample?

I've got a mysql table that has a running total:
+---------------------+--------+
| Timestamp | Total |
+---------------------+--------+
| 2012-07-04 05:35:00 | 1.280 | 1.280-1.280 = 0
| 2012-07-04 09:25:00 | 2.173 | 2.173-1.280 = 0.893
| 2012-07-04 09:30:00 | 2.219 | 2.219-1.280 = 0.939
| 2012-07-04 15:00:00 | 7.778 | 7.778-1.280 = 6.498
| 2012-07-04 21:05:00 | 13.032 | 13.032-1.280 = 11.752
| 2012-07-04 22:00:00 | 13.033 | 13.033-1.280 = 11.753
| 2012-07-05 05:20:00 | 13.033 | 13.033-13.033 = 0
| 2012-07-05 07:10:00 | 13.140 | 13.140-13.033 = 0.107
| 2012-07-05 10:15:00 | 14.993 | 14.993-13.033 = 1.960
| 2012-07-05 11:35:00 | 16.870 | 16.870-13.033 = 3.837
+---------------------+--------+
What I'm looking for is a query that determines the aggregated daily increase for each interval.
I've tried to show the desired outcome as well as the calculation behind each row. I've tried already several things with a join, but somehow I fail to determine what the starting value for each day is.
Thanks.
I can't vouch for the efficiency of this query, but it does get you the results you are looking for:
SELECT t1.`Timestamp`, t1.`Total`,
CASE WHEN t1.`timestamp` =
(SELECT MIN(t2.`Timestamp`)
FROM myTable t2
WHERE DATE(t2.`Timestamp`)=DATE(t1.`Timestamp`))
THEN 0
ELSE t1.`Total` - (SELECT MIN(t3.`Total`)
FROM myTable t3
WHERE DATE(t3.`Timestamp`)=DATE(t1.`Timestamp`))
END AS Diff
FROM myTable t1
ORDER BY `Timestamp`
Alternate Solution (more efficient I think)
SELECT t1.`Timestamp`, t1.`Total`, (t1.`Total` - d1.MinVal) diff
FROM myTable t1
INNER JOIN
(SELECT DATE(`Timestamp`) ts_date,
MIN(`Total`) AS MinVal
FROM myTable
GROUP BY ts_date) d1
ON DATE(t1.`Timestamp`) = d1.ts_date