I got an sql issue. I have two tables which look like this:
first TABLE X second TABLE Y
TabX_ID| DATE | Value Z TabY_ID|TabX_ID | DATE | Value X | Value Y
4711 | 15.01 | 12 1 | 4711 | 15.01| 123 | 876
4711 | 20.01 | 5 2 | 4711 | 16.01| 12 | 54
4711 | 25.01 | 67 3 | 4711 | 17.01| 23 | 38
4 | 4711 | 20.01| 56 | 13
5 | 4711 | 23.01| 1 | 5
I need to assing all the data from TABLE Y to the data in the TABLE X DATE to the fitting
timeframe.
I cant use a simple min - max because it changes.
1. DATE min 15.01 DATE-max:19.01
2. DATE-min:20.01 DATE-max:24.01
3. DATE-min:25.01 DATE-max:...
So it looks like this
1 | 15.01 | 123 | 876
4711 | 15.01 | 12 -> 2 | 16.01 | 12 | 54
3 | 17.01 | 23 | 38
4711 | 20.01 | 5 -> 4 | 20.01 | 56 | 13
5 | 23.01 | 1 | 5
First I need to perform calculations with the TABLE Y VALUES X an Y and after that I need the VALUE Z
from TABLE X. So it looks like this:
ID | DATE | Calculated_Val
4711| 15.01 | 345
4711| 20.01 | 892
Is there a way to do this?
thx in advance
Not sure about MySQL but if you are doing this with Oracle, I would use the LEAD analytic function to get the next date value in the future in tableX and then join that to tableY.
An example of this would be:
select
tabX_id,
date_val as min_date,
next_date_val as max_date,
valueZ,
valueX,
valueY,
y.date_val as tabY_date
from (
select
tabX_id,
date_val,
lead(date_val) over (partition by tabx_id order by date_val)
as next_date_val,
valueZ
from tabX
) x
join tabY y on (x.tabX_id = y.tabX_id and
y.date_val >= x.date_val and
(x._next_date_val is null or y.date_val < x.next_date_val))
Note that I haven't modified the next value of the date so am using a less-than condition. This is probably appropriate if you have a time component in any of the date fields but might not be exactly what you want if they are just date value.
This is a simple join and group by:
select x.TabX_ID, y.DATE, min(ValueX), min(ValueY)
from TableX x
join TableY y
on x.TabX_ID = y.TabX_ID
and x.DATE = y.DATE
group by x.TabX_ID, y.DATE
Related
I am new to SQL, would like to have your suggestions on how to solve this problem,
I have the sales information by type
I want to sum the Prices of certain references by Type and based on the resulting sum, fetch the values from another table and populate in the Output Column.
Group Type 100000 200000 300000
1 A 1 2 3
1 B 0 1 1
2 T 2 2 4
2 U 0 2 2
3 V 2 2 3
4 N 1 1 1
From the above table 2 we find the TYPE A and B belong to same group - Group 1. So in the first table, the query should sum Prices of the references belonging to the Group 1. If the sum is >100000 and <=200000 then based on the type the corresponding value must be chosen.
Incase the sum of Prices based on group is less than 100000 or the type not found in Table 2 then it should take the values from the below table
[+------+----+---+
| Type | 1 | 2 |
+------+----+---+
| A | 50 | 2 |
| B | 60 | 5 |
| C | 65 | 2 |
| D | 65 | 3 |
| E | 65 | 4 |
+------+----+---+][3]
Thus the final output for the above datasheet would be like below,
Order ID Reference Type Price Output
101 AAA A 500000 3
101 AAB B 100000 1
101 ABC C 20000 67
101 DCE B 50000 1
101 BOD D 200000 68
101 ZYZ E 200000 69
102 AAA A 20000 52
So for the first line, its TYPE A and Type A is present under Group 1 and in Group1 we also have Type 2. So for the same order ID 101 , the overall Sales of Type A and B is 650000 > 300000, therefore for Type A we chose the value 3 from the table 2. Since Type C is not present in Table 2, I went to Table 3 and added the two values and so on
Sorry for the long post. Hope my question is clear? Would like to have your expert opinion.
Thanks,
SS
Join all tables and make sure you do LEFT JOIN as we want to keep records from the first table even we don't have corresponding data in the second or third table.
For total count, give priority to the second table, use case when to verify in which range this mrp field is falling. If lies within a range pick count from the second table otherwise pick count from the third table.
SELECT
s.order_id,
s.reference,
s.`type`,
s.mrp,
#a:= IFNULL(g_total.Total, s.mrp) AS MRP_Total, -- #a variable to use it in CASE WHEN clause
CASE
WHEN #a > 100000 AND #a <= 200000 AND sg.`type` IS NOT NULL THEN sg.price_100000
WHEN #a > 200000 AND #a <= 300000 AND sg.`type` IS NOT NULL THEN sg.price_200000
WHEN #a > 300000 AND sg.`type` IS NOT NULL THEN sg.price_300000
ELSE tp.price_1 + tp.price_2
END Total
FROM sales s
LEFT JOIN sales_group sg ON s.`type` = sg.`type`
LEFT JOIN type_prices tp ON s.`type` = tp.`type`
LEFT JOIN (
SELECT
s.order_id, sgg.`group`, SUM(mrp) as Total
FROM sales s
INNER JOIN sales_group sgg ON s.`type` = sgg.`type`
GROUP BY s.order_id, sgg.`group`
) AS g_total -- Temp table to find total MRP, order and group wise
ON s.order_id = g_total.order_id AND sg.`group` = g_total.`group`
ORDER BY s.order_id, s.`type`;
Output:
sales
---
| order_id | reference | type | mrp | MRP_Total | Total |
---------------------------------------------------------
| 101 | AAA | A | 500000 | 650000 | 3 |
| 101 | DCE | B | 50000 | 650000 | 1 |
| 101 | AAB | B | 100000 | 650000 | 1 |
| 101 | ABC | C | 200000 | 200000 | 67 |
| 101 | BOD | D | 200000 | 200000 | 68 |
| 101 | ZYZ | E | 200000 | 200000 | 69 |
| 102 | AAA | A | 20000 | 20000 | 52 |
Note: sg.type IS NOT NULL is added in CASE WHEN clause because if we don't have any mapping in the second table, we should move to ELSE part which refers to the third table.
I came across a task where I have to return the total COUNT and SUM of issued policies for each day of the month and compare it to the previous year.
Table PolicyOrder has fields:
PolicyOrderId - primary key
CreatedAt (DATETIME)
CalculatedPremium - cost of policy or "premium"
PolicyOrderStatusId - irrelevant to the question but still - status of the policy.
To solve this I came up with a query that inner joins self table and sums/counts by grouping according to DAY of the creation date.
SELECT
DATE(po1.CreatedAt) AS dayDate_2017,
SUM(po1.CalculatedPremium) AS premiumSum_2017,
COUNT(po1.PolicyOrderId) AS policyCount_2017,
po2.*
FROM
PolicyOrder po1
INNER JOIN (
SELECT
DATE(CreatedAt) AS dayDate_2018,
SUM(CalculatedPremium) AS premiumSum_2018,
COUNT(PolicyOrderId) AS policyCount_2018
FROM
PolicyOrder po2
WHERE
YEAR(CreatedAt) = 2018 AND
MONTH(CreatedAt) = 10 AND
PolicyOrderStatusId = 6
GROUP BY
DAY(CreatedAt)
) po2 ON (
DAY(po2.dayDate_2018) = DAY(po1.CreatedAt)
)
WHERE
YEAR(po1.CreatedAt) = 2017 AND
MONTH(po1.CreatedAt) = 10 AND
PolicyOrderStatusId = 6
GROUP BY
DAY(po1.CreatedAt)
The above query returns these results:
dayDate_2017 | premiumSum_2017 | policyCount_2017 | dayDate_2018 | premiumSum_2018 | policyCount_2018
2017-10-01 | 4699.36 | 98 | 2018-10-01 | 8524.21 | 144
2017-10-02 | 9114.55 | 168 | 2018-10-02 | 7942.25 | 140
2017-10-03 | 9512.43 | 178 | 2018-10-03 | 9399.61 | 161
2017-10-04 | 9291.77 | 155 | 2018-10-04 | 6922.83 | 137
2017-10-05 | 8063.27 | 155 | 2018-10-05 | 9278.58 | 178
2017-10-06 | 9743.40 | 184 | 2018-10-06 | 6139.38 | 136
...
2017-10-31 | ...
The problem is that now I have to add two more columns in which policies has to be counted and amounts added from the start of the year UP UNTIL each returned row.
Desired results:
dayDate_2017 | premiumSum_2017 | policyCount_2017 | sumFromYearBegining | countFromYearBegining
2017-10-01 | 4699.36 | 98 | 150000.34 | 5332
2017-10-02 | 9114.55 | 168 | 156230.55 | 5443
2017-10-03 | 9512.43 | 178 | 160232.44 | 5663
...
2017-10-31 | ...
WHERE:
sumFromYearBegining (150000.34) - SUM of premiumSum from 2017-01-01 until 2017-10-01 (excluding)
countFromYearBegining (5332) - COUNT of policies from 2017-01-01 until 2017-10-01 (excluding)
sumFromYearBegining (1566239.55) - SUM of premiumSum from 2017-01-01 until 2017-10-02 (excluding)
countFromYearBegining (5443) - COUNT of policies from 2017-01-01 until 2017-10-02 (excluding)
sumFromYearBegining (160232.44) - SUM of premiumSum from 2017-01-01 until 2017-10-02 (excluding)
countFromYearBegining (5663) - COUNT of policies from 2017-01-01 until 2017-10-02 (excluding)
I have tried inner joining same table COUNTed and SUMed which failed because I cannot specify the range up to which I need to count and sum, I have tried LEFT joining and then counting, which fails because the results are counted not untill each row result but until the last result etc...
DB Fiddle: https://www.db-fiddle.com/f/ckM8HyTD6NjLbK41Mq1gct/5
Any help from you SQL ninjas highly appreciated.
We can use User-defined variables to calculate Rolling Sum / Count, in absence of Window Functions' availability.
We will first need to determine the Sum and Count for every day in the year 2017 (even though you need rows for a particular month only). Because, in order to calculate rolling Sum for the days in March month, we would need the sum/count values from the January, and February month(s) as well. One optimization possibility is that we can restrict calculations from the first month to the require month only.
Note that ORDER BY daydate_2017 is necessary in order to be able to calculate rolling sum correctly. By default, data is in unordered fashion. Without defining the order, we cannot guarantee that Sum will be correct.
Also, we need to two levels of sub-select queries. First level is used to calculate the Rolling sum values. Second level is used to restrict the result to February month only. Since WHERE is executed before SELECT; we cannot restrict the result to February month, in the first level itself.
If you need similar rolling Sum for the year 2018 as well; similar query logic can be implemented in other set of sub-select queries.
SELECT dt2_2017.*, dt_2018.*
FROM
(
SELECT dt_2017.*,
#totsum := #totsum + dt_2017.premiumsum_2017 AS sumFromYearBegining_2017,
#totcount := #totcount + dt_2017.policycount_2017 AS countFromYearBeginning_2017
FROM (SELECT Date(po1.createdat) AS dayDate_2017,
Sum(po1.calculatedpremium) AS premiumSum_2017,
Count(po1.policyorderid) AS policyCount_2017
FROM PolicyOrder AS po1
WHERE po1.policyorderstatusid = 6 AND
YEAR(po1.createdat) = 2017 AND
MONTH(po1.createdat) <= 2 -- calculate upto February for 2017
GROUP BY daydate_2017
ORDER BY daydate_2017) AS dt_2017
CROSS JOIN (SELECT #totsum := 0, #totcount := 0) AS user_init_vars
) AS dt2_2017
INNER JOIN (
SELECT
DATE(po2.CreatedAt) AS dayDate_2018,
SUM(po2.CalculatedPremium) AS premiumSum_2018,
COUNT(po2.PolicyOrderId) AS policyCount_2018
FROM
PolicyOrder po2
WHERE
YEAR(po2.CreatedAt) = 2018 AND
MONTH(po2.CreatedAt) = 2 AND
po2.PolicyOrderStatusId = 6
GROUP BY
dayDate_2018
) dt_2018 ON DAY(dt_2018.dayDate_2018) = DAY(dt2_2017.dayDate_2017)
WHERE YEAR(dt2_2017.daydate_2017) = 2017 AND
MONTH(dt2_2017.daydate_2017) = 2;
RESULT: View on DB Fiddle
| dayDate_2017 | premiumSum_2017 | policyCount_2017 | sumFromYearBegining_2017 | countFromYearBeginning_2017 | dayDate_2018 | premiumSum_2018 | policyCount_2018 |
| ------------ | --------------- | ---------------- | ------------------------ | --------------------------- | ------------ | --------------- | ---------------- |
| 2017-02-01 | 4131.16 | 131 | 118346.77 | 3627 | 2018-02-01 | 8323.91 | 149 |
| 2017-02-02 | 2712.74 | 85 | 121059.51000000001 | 3712 | 2018-02-02 | 9469.33 | 153 |
| 2017-02-03 | 3888.59 | 111 | 124948.1 | 3823 | 2018-02-03 | 6409.21 | 97 |
| 2017-02-04 | 2447.99 | 74 | 127396.09000000001 | 3897 | 2018-02-04 | 5693.69 | 120 |
| 2017-02-05 | 1437.5 | 45 | 128833.59000000001 | 3942 | 2018-02-05 | 8574.97 | 129 |
| 2017-02-06 | 4254.48 | 127 | 133088.07 | 4069 | 2018-02-06 | 8277.51 | 133 |
| 2017-02-07 | 4746.49 | 136 | 137834.56 | 4205 | 2018-02-07 | 9853.75 | 173 |
| 2017-02-08 | 3898.05 | 125 | 141732.61 | 4330 | 2018-02-08 | 9116.33 | 144 |
| 2017-02-09 | 8306.86 | 286 | 150039.46999999997 | 4616 | 2018-02-09 | 8818.32 | 166 |
| 2017-02-10 | 6740.99 | 204 | 156780.45999999996 | 4820 | 2018-02-10 | 7880.17 | 134 |
| 2017-02-11 | 4290.38 | 133 | 161070.83999999997 | 4953 | 2018-02-11 | 8394.15 | 180 |
| 2017-02-12 | 3687.58 | 122 | 164758.41999999995 | 5075 | 2018-02-12 | 10378.29 | 171 |
| 2017-02-13 | 4939.31 | 159 | 169697.72999999995 | 5234 | 2018-02-13 | 9383.15 | 160 |
If you want a way that avoids using #variables in the select list, and also avoids analytics (only mysql 8 supports them) you can do it with a semi-cartesian product:
WITH prevYr AS(
SELECT
YEAR(CreatedAt) AS year_prev,
MONTH(CreatedAt) AS month_prev,
DAY(CreatedAt) AS day_prev,
SUM(CalculatedPremium) AS premiumSum_prev,
COUNT(PolicyOrderId) AS policyCount_prev
FROM
PolicyOrder
WHERE
CreatedAt BETWEEN '2017-02-01' AND '2017-02-28' AND
PolicyOrderStatusId = 6
GROUP BY
YEAR(CreatedAt), MONTH(CreatedAt), DAY(CreatedAt)
),
currYr AS (
SELECT
YEAR(CreatedAt) AS year_curr,
MONTH(CreatedAt) AS month_curr,
DAY(CreatedAt) AS day_curr,
SUM(CalculatedPremium) AS premiumSum_curr,
COUNT(PolicyOrderId) AS policyCount_curr
FROM
PolicyOrder
WHERE
CreatedAt BETWEEN '2018-02-01' AND '2018-02-28' AND
PolicyOrderStatusId = 6
GROUP BY
YEAR(CreatedAt), MONTH(CreatedAt), DAY(CreatedAt)
)
SELECT
*
FROM
prevYr
INNER JOIN
currYr
ON
currYr.day_curr = prevYr.day_prev
INNER JOIN
(
SELECT
main.day_prev AS dayRolling_prev,
SUM(pre.premiumSum_prev) AS premiumSumRolling_prev,
SUM(pre.policyCount_prev) AS policyCountRolling_prev
FROM
prevYr main LEFT OUTER JOIN prevYr pre ON pre.day_prev < main.day_prev
GROUP BY
main.day_prev
) rollingPrev
ON
currYr.day_curr = rollingPrev.dayRolling_prev
ORDER BY 1,2,3
We summarise the year 2017 and year 2018 data into two CTEs because it makes things a lot cleaner and neater later, particularly for this rolling count. You can probably follow the logic of the CTE easily because it's lifted more or less straight from your query - I only dropped the DATE column in favour of a year/month/date triplet because it made other things cleaner (joins) and can be recombined to a date if needed. I also swapped the WHERE clauses to use date BETWEEN x AND y because this will leverage an index on a column whereas using YEAR(date) = x AND MONTH(date) = y might not
The rolling counts works via something I referred to as a semi-cartesian. It's actually a cartesian product; any database join that results in rows from one o both tables multiplying and being represented repeatedly in the output, is a cartesian product. Rather than being a full product (every row crossed with every other row) in this case it uses a less than, so every row is only crossed with a subset of rows. As the date increases, more rows match the predicate, because a date of 30th has 29 rows that are less than it.
This thus causes the following pattern of data:
maindate predate maincount precount
2017-02-01 NULL 10 NULL
2017-02-02 2017-02-01 20 10
2017-02-03 2017-02-01 30 10
2017-02-03 2017-02-02 30 20
2017-02-04 2017-02-01 40 10
2017-02-04 2017-02-02 40 20
2017-02-04 2017-02-03 40 30
You can see that for any given main date, it repeats N - 1 times because there are N - 1 dates lower than in that satisfy the join condition predate < maindate
If we group by the maindate and sum the counts associated with each predate, we get the rolling sum of all the pre-counts on that main-date (So, on the 4th day of the month, it's SUM(pre count for dates 1st - 3rd, i.e. 10+20+30 = 60. On the 5th day, we sum the counts for days 1 to 4. On the 6th day, we sum days 1 to 5 etc)
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;
I have two tables. One table stores basic information on inventory items like id and description. The other table stores the status of the inventory items at the end of each day; so there are many rows in this table with the same inventoryItemID but different dates. Below is basically what my tables look like.
TABLE: listItems
|itemRecordNumber|itemName|minStock|reorderQty|note|
| 510 | bag | 10 | 20 | |
| 511 | ball | 20 | 40 | |
| 512 | shoe | 09 | 10 | xx |
,
TABLE: inventoryTrans
|itemRecordNumber|recordType|quantity| trans_date |
| 510 | 50 | 30 | 09/12/2013 |
| 510 | 40 | 33 | 09/12/2013 |
| 510 | 50 | 35 | 08/12/2013 |
| 510 | 40 | 35 | 08/12/2013 |
| 511 | 50 | 10 | 08/12/2013 |
| 511 | 40 | 15 | 09/12/2013 |
| 512 | 50 | 33 | 07/12/2013 |
| 512 | 40 | 34 | 09/12/2013 |
Now, what i want is, the most recent record from the table inventoryTrans for each itemRecordNumber and only the records of type 50(a sale), but wait theres more, if the note in the table listItems on an item is 'xx' i am not interested in that result. What i really need from this table is the quantity. Notice this table does not have the items name / minStock / reorderQty , which is why i need the first table listItems, to get that info.
So in a single query i would like to get,
|itemRecordNumber|itemName| trans_date |quantity|minStock|reorderQty|
| 510 | bag | 09/12/2013 | 30 | 10 | 20 |
| 511 | ball | 08/12/2013 | 10 | 20 | 40 |
So what i want is this information. Notice it is the most recent of the recordType '50' for each ID, yet item 512 was excluded due to the note on the item being 'xx' in listItems.note ;
Now before you criticize the construct of the tables, it is not something I can change. This is how the tables for Peachtree accounting(the software my company at hand is using) is layed out, so this is what I have to work with. The goal is to generate custom information on inventory for better management.
Now how can I construct a SQL query for this. i have seen people use LEFT JOIN in some other similar examples but i cant get it to work.
Here we go
http://sqlfiddle.com/#!2/d30823/1 this is the example with your data
SELECT a.itemRecordNumber,
a.itemName,
max(b.trans_date) AS trans_date,
b.quantity,
a.minStock,
a.reorderQty
FROM listItems a,
inventoryTrans b
WHERE a.itemRecordNumber = b.itemRecordNumber
AND a.note<>'xx'
AND b.recordtype=50
GROUP BY a.itemRecordNumber
Edit:
In case of MS Access (which I am unable to test), this should work, can you try (I did try in MS SQL which is the closest I can test in SQLFiddle
http://sqlfiddle.com/#!3/d30823/2
SELECT a.itemRecordNumber,
a.itemName,
max(b.trans_date) AS trans_date,
b.quantity,
a.minStock,
a.reorderQty
FROM listItems a,
inventoryTrans b
WHERE a.itemRecordNumber = b.itemRecordNumber
AND a.note<>'xx'
AND b.recordtype=50
AND b.trans_date =
(SELECT max(trans_date)
FROM inventoryTrans c
WHERE c.itemRecordNumber = b.itemRecordNumber
AND c.recordtype=50)
GROUP BY a.itemRecordNumber,
a.itemName,
b.quantity,
a.minStock,
a.reorderQty
Let me know if it does not work for you for any reason, I got the same results as your question
Though this is not the most efficient way, it should work.
SELECT l.*, i.trans_date, i.quantity
FROM listItems as l
JOIN (SELECT i.itemRecordNumber, MAX(trans_date) as date
FROM inventoryTrans as i GROUP BY i.itemRecordNumber
) as x ON x.itemRecordNumber = l.itemRecordNumber
JOIN inventoryTrans as i ON l.itemRecordNumber = i.itemRecordNumber
AND i.trans_date = x.date
WHERE i.recordType = 50
Though this will give a row for itemRecordNumber = 512 too, if you do not need it you can add a WHERE and filter according to your need.
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