Replace null value by latest value - sql-server-2008

Say I have below table. How do I pick up the latest previous value in case my joined table to not match the date and currency? On the null DKK value I want it to pick up 3. Note that dates do not exist every day since I do not load the tables on weekends.
Select
PositionDate,
Currency,
T2.Value,
isnull(t2.value, ? )
From t1
left join t2
on t1.currency = t2.Currency
and t1.PositionDate = t2.PositionDate
.
PositionDate Currency Value
2017-04-11 SEK 1
2017-04-11 DKK NULL
2017-04-11 EUR 7
2017-04-10 SEK 4
2017-04-10 DKK 3
2017-04-10 EUR 5
2017-04-07 SEK 4
2017-04-07 DKK 3
2017-04-07 EUR 5
.

This will work for your case i think:
SELECT
t1.PositionDate,
t1.Currency,
COALESCE(t1.Value,t2.value) AS Value
FROM t1
LEFT join (SELECT MAX(PositionDate) AS PositionDate,Currency FROM t2 WHERE PositionDate < t1.PositionDate GROUP BY Currency) tjoin
LEFT join t2 on tjoin.currency = t2.Currency and tjoin.PositionDate = t2.PositionDate

You can Achieve it by using CTE and Case condition.
With cte as
(
Select
PositionDate,
Currency,
T2.Value,
From t1
left join t2
on t1.currency = t2.Currency
and t1.PositionDate = t2.PositionDate
and t1.PositionDate = t2.PositionDate
)
select PositionDate, Currency, Value,
CASE WHEN ISNULL(value,'')='' THEN
(Select top 1 from cte cin where cin.currency=cout.currency order by CONVERT(Date,PositionDate) desc)
ELSE
Value
END as Value2
From cte cout

Related

mysql LEFT JOIN followed by GROUP BY with BETWEEN Ranges

I am stumped with how I should proceed. Here is my current LEFT JOIN command which works just fine:
SELECT t1.avg_temperature as T_aver,
t2.new_confirmed as count
FROM 3_day_avg as t1
LEFT JOIN table2 as t2 on t1.date = t2.date
And this works great to make this table:
T_aver |count|
-----------------
-0.2 | 2 |
3 | 2 |
5 | 1 |
-2.3 | 4 |
22 | 0 |
But now I want to take it one step further and group by ranges of T_aver (bins like 0-5, 6-10, 11-15, etc) and SUM() the count column. For example, If I was to place the range on the LEFT JOIN table example above of -10 to 0, and 0 to 30, the final table would look like this:
Trange |count|
-----------------
-10 - 0 | 6 |
0 - 30 | 3 |
This above transformation is where I am stumped and I fear to make my life simpler I just need to create one big table instead...
Thanks in advance
You were very close! Just start with your existing query and then wrap it.
For your bins use another lookup table if you don't want to be bound to a fixed interval:
bins:
mintemp | maxtemp
-10 | 0
0 | 30
I will use num instead of count, as I never use reserved words as columns:
SELECT
CONCAT(mintemp, ' - ', maxtemp) AS Trange,
SUM(baseview.num) AS num
FROM bins
INNER JOIN (
SELECT
t1.avg_temperature as T_aver,
t2.new_confirmed as num
FROM 3_day_avg as t1
LEFT JOIN table2 as t2 on t1.date = t2.date
) AS baseview
ON baseview.T_aver>bins.mintemp AND baseview.T_aver<=bins.maxtemp
GROUP BY bins.mintemp;
SELECT ranges.caption Trange,
SUM(t2.new_confirmed) as `count`
FROM 3_day_avg as t1
LEFT JOIN table2 as t2 on t1.date = t2.date
JOIN ( SELECT -10 t_from, 0 t_to, '-10 - 0' caption
UNION ALL
SELECT 0, 30, '0 - 30' ) ranges ON t1.avg_temperature >= ranges.t_from
AND t1.avg_temperature < ranges.t_to
GROUP BY ranges.caption;
It is better to create static ranges table instead of dynamically generated. This allows to create and to store a lot of pre-defined sets of ranges.
You can group by a CASE expression which contains your bins:
SELECT
CASE
WHEN t1.avg_temperature >= -10 and t1.avg_temperature <= 0 THEN '-10 - 0'
WHEN t1.avg_temperature > 0 and t1.avg_temperature <=30 THEN '0 - 30'
END AS Trange,
SUM(t2.new_confirmed) AS count
FROM 3_day_avg AS t1 LEFT JOIN table2 AS t2
ON t1.date = t2.date
GROUP BY Trange
You may add more bins in the CASE expression, change the ranges and the inequality signs to suit your requirement.

select id from table with max date and max id if dates are the same

For an example I have MySQL table like:
id category date value
1 a 2013-01-02 7
2 a 2013-01-01 2
3 a 2013-01-01 3
4 b 2013-01-01 1
5 b 2013-01-02 4
6 b 2013-01-03 5
7 c 2013-01-03 4
8 c 2013-01-03 8
I need select records with MAX(date) for each category, but if date is the same, with MAX(id).
So, expected result must be:
id category date value
1 a 2013-01-02 7
6 b 2013-01-03 5
8 c 2013-01-03 8
As you can see, dates for same category can be not sorted (like id=4,5).
I tried query like:
SELECT * FROM mytable t1
INNER JOIN
(
SELECT category, MAX(date) AS MAXDATE
FROM mytable
GROUP BY category
) t2
ON t1.category = t2.category
AND t1.date = t2.MAXDATE
It works fine, when we have no same dates in table. But for my case, it will return records with lowest ids:
id category date value
1 a 2013-01-01 7
6 b 2013-01-03 5
7 c 2013-01-03 4
You are on the right track but you need to change your join.
I think what you want is the following:
SELECT * FROM mytable t1
INNER JOIN
(
SELECT MAX(id) AS ID, MAX(date) AS MAXDATE
FROM mytable
GROUP BY category
) t2
ON t1.id = t2.ID
AND t1.date = t2.MAXDATE
As mentioned, the previous wont work for every use case. The following might.
SELECT a.*
FROM mytable AS a,
(
SELECT category, date, MAX(id) AS ID
FROM mytable
GROUP BY category, date
) AS b,
(
SELECT category, MAX(date) AS date
FROM mytable
GROUP BY category
) AS c
WHERE b.category = c.category
AND b.date = c.date
AND a.id = b.id

mysql select column outside group by

I have the following dataset
ID P C1 C2 XR
1 1 GBP USD 1.6
2 2 GBP USD 1.7
3 1 GBP EUR 0.84
4 2 GBP EUR 0.83
5 1 GBP CHF 1.8
6 2 GBP CHF 1.8
I'm trying to get the minimum XR for each pair so the results should be
ID P C1 C2 XR
1 1 GBP USD 1.6
4 2 GBP EUR 0.83
5 1 GBP CHF 1.8
but when I SELECT MIN(XR) GROUP BY C1,C2, I either dont get the correct ID or get a warning saying ID not in GROUP BY if 'ONLY_FULL_GROUP_BY' enabled
Closest I've got is this:
SELECT t1.id, t1.C1, t1.C2, MIN(t2.XR) FROM xrates t1
LEFT JOIN xrates AS t2 ON t1.id = t2.id
GROUP BY t1.id, t1.C1, t1.C2
HAVING t1.C1 = 'GBP' AND t1.C2 IN ('USD','EUR')
select rates.*
from xrates rates
where id in
(
select min(xrates.id) as minid
from xrates
inner join
(
select C1, C2, MIN(XR) as minxr
from xrates
GROUP BY C1, C2
HAVING C1 = 'GBP'
) x on x.c1 = xrates.c1 and x.c2 = xrates.c2 and x.minxr = xrates.xr
group by xrates.C1, xrates.C2
)
SQLFiddle demo
SELECT x.*
FROM my_table x
JOIN
(SELECT grouping _id,MIN(ordering_id) min_ordering_id FROM my_table GROUP BY grouping_id) y
ON y.grouping_id = x.grouping_id
AND y.min_ordering_id = x.ordering_id;
(note that the 'grouping_id' may be formed on more than one column!)
You don't need to join the table to itself, and you do not want to GROUP BY the id or every row is going to be unique. The following should return the results you specify:
SELECT
xrates.id,
xrates.C1,
xrates.C2,
MIN(xrates.XR)
FROM xrates
WHERE xrates.C1 = 'GBP'
AND xrates.C2 IN ('USD','EUR')
GROUP BY
xrates.C1,
xrates.C2;

Group dates based on variable periods

i have two tables as follows------
table-1
CalenderType periodNumber periodstartdate
1 1 01-01-2013
1 2 11-01-2013
1 3 15-01-2013
1 4 25-01-2013
2 1 01-01-2013
2 2 15-01-2013
2 3 20-01-2013
2 4 25-01-2013
table2
Incidents Date
xyz 02-01-2013
xxyyzz 03-01-2013
ccvvb 12-01-2013
vvfg 16-01-2013
x3 17-01-2013
x5 24-01-2013
Now i want to find out the number of incidents took place in a given period(the Calendar type may change on runtime like)
the query should look something like this
select .......
from ......
where CalendarType=1
which should return
CalendarType PeriodNumber Incidents
1 1 2
1 2 1
1 3 3
1 4 0
can someone suggest me an approach or any method how this can be achieved.
Note:each period is variable in size.peroid1 may have 10 days period2 may have 5 days etc.
I think this does what you want, although I don't understand how you arrived at your sample output:
select t.CalenderType, t.periodNumber, count(*) as Incidents
from Table1 t
inner join (
select t2.Date, t2.Incidents, max(t1.periodstartdate) as PeriodStartDate
from Table2 t2
inner join Table1 t1 on t2.Date >= t1.periodstartdate
where CalenderType = 1
group by t2.Date, t2.Incidents
) a on t.periodstartdate = a.PeriodStartDate
where CalenderType=1
group by t.CalenderType, t.periodNumber
SQL Fiddle Example
Try this, a bit more general solution,SQLFiddle (Thanks RedFilter for schema):
SELECT t1.CalenderType, t1.periodNumber, count(Incidents)
FROM Table1 t1, Table1 t11, Table2
WHERE
(
(
t1.CalenderType = t11.CalenderType
AND t1.periodNumber = t11.periodNumber - 1
AND Date BETWEEN t1.periodstartdate AND t11.periodstartdate
)
OR
(
t1.periodNumber = (SELECT MAX(periodNumber) FROM Table1 WHERE t1.CalenderType = CalenderType)
AND Date > t1.periodstartdate
)
)
GROUP BY t1.CalenderType, t1.periodNumber
ORDER BY t1.CalenderType, t1.periodNumber

select from one table where count=1 in second table and two more conditions

I need to select transactions with cash payment and same date but only for those transactions
which have only one payment ( so num 14 should be omitted from resultset )
So correct result is 12 and 13 only.
Table2 Table1
num | date | data | total num | payment | date
12 xy abc 2.5 12 cash xy
13 xy cbc 2.1 13 cash xy
14 xy acc 2.3 14 visa xy
19 xy def 2.0 14 cash xy
27 xy fgh 1.3 19 visa xy
27 mc xy
Something like this gives num 14 in result-set but 14 should be omitted.
SELECT num, data
FROM Table2
WHERE num IN
(
SELECT num FROM `Table1`
WHERE payment = 'cash'
GROUP BY `num`
HAVING ( COUNT(`num`) = 1 )
)
To sumarize correct answer (by tombom ):
SELECT t2.num, t2.data
FROM Table1 as t1
INNER JOIN Table2 as t2 ON t1.num = t2.num
AND t1.date = 'xy'
GROUP BY t1.num
HAVING GROUP_CONCAT(t1.payment) = 'cash'
Thanks!
I'm sorry, I totally misunderstood your question. Here's how it works:
SELECT
*
FROM
Table1 t1
INNER JOIN Table2 t2 ON t1.num = t2.num AND t1.date = t2.date
GROUP BY t1.num
HAVING GROUP_CONCAT(t1.payment) = 'cash'