Join with DateTime and Latest Value from another Table - sql

I have a small database with the data from weather parameters being recorded daily. This is a Microsoft SQL Server 2008 express Database
My tables are as follows:
station (id, name, position)
reading (station_id, timestamp, value)
--station_id is the foreign key to id in station table
I want to Join them and get the result as below:
id | name | value | time
--------+---------+-------------------------
0 | lake | 73 |2013/08/16 02:00
1 | pier | 72 |2013/08/16 02:00
2 | gate | 81 |2013/08/16 02:00
Looking at question like Join to only the "latest" record with t-sql, I've been only able to get one row from the first table, and using Join two tables, only use latest value of right table, I've been able to get only the max time from second table.
How can I get the output that I want?

It can be done with a subquery
SELECT s.id,
s.name,
r.value,
r.timestamp
FROM station as s
INNER JOIN reading as r
on s.id = r.station_id
WHERE r.timestamp = (
SELECT max(timestamp)
FROM reading
where reading.station_id = s.station_id
)

SELECT STATION.ID,STATION.Name,T2.timestamp,T2.Value
FROM STATION
LEFT JOIN
(
SELECT station_id,timestamp, value
FROM
(
SELECT station_id,timestamp, value,
ROW_NUMBER() OVER (PARTITION BY station_id ORDER BY timestamp DESC) as rn
FROM reading
) as T1
WHERE RN=1
) as T2 on STATION.ID=T2.station_id

Related

Select top entry per ID from data table - SQL

Assuming I have the following data table:
ID VALUE
--- -------
123 a
123 b
456 c
456 d
789 e
How can I get the output to only select the top entry per ID, assuming the data is already ordered. Ie, I need the data to show:
ID VALUE
--- -------
123 a
456 c
789 e
SQL tables represent unordered sets. There is no ordering to the table, unless you have a column that specifies the ordering.
This is fundamental in SQL. Let me assume you have an ordering column. Then you can do:
select t.*
from t
where t.ordcol = (select max(t2.ordcol) from t t2 where t2.id = t.id);
Also you can add the ordering dynamically,
e.g.
select * from
(select id, value, dense_rank() OVER (ORDER BY value asc) as myrank from #mytable ) aa
where myrank=1

Updating Data having same id but different Data in Row

I have a record with same ID but different data in both rows
While updating the final result should be the last record of that ID present in data.
Example
ID | Name | PermanentAddrss | CurrentLocation
1 | R1 | INDIA | USA
1 | R1 | INDIA | UK
Now for ID 1 the record which will be loaded in database
1|R1|INDIA|UK
How this can be done in SQL server for multiple records?
Please understand that SQL server does not store or fetch data in order of data insertion, so to find the latest/last record you should have some way to order the records.
This is typically a timestamp column like last_modified_date. Your current table is prime candidate for a slow changing dimension type 2; and you should consider implementing it.
See explanation on Kimball's group site.
If you are really not affected by any order and just need a row for each id you can try below query.
select
ID,
Name,
PermanentAddress,
CurrentLocation
from
(select
*,
row_number() over(partition by id order by (select null)) r
from yourtable)t
where r=1
You can identify the latest ID value by:
SELECT B.ID, A.NAME, A.PERMANENTADDRS, A.CURRENTLOCATION
FROM
(SELECT ID, NAME, PERMANENTADDRS, CURRENTLOCATION, MAX(RNUM) AS LATEST_ID FROM
(SELECT ID, NAME, PERMANENTADDRS, CURRENTLOCATION, ROW_NUMBER() OVER (PARTITION BY ID) AS RNUM FROM YOUR_TABLE)
GROUP BY ID, NAME, PERMANENTADDRS, CURRENTLOCATION) A
INNER JOIN
YOUR_TABLE B
ON A.LATEST_ID = B.ID;
This will take the last populated record for a given ID value. If the logic for latest record is different, it can be appropriately incorporated in the query.

Select particular not grouped column from grouped set

The topic might be a little bit unclear but I couldn't describe in a single sentence what I want to achieve.
Say I have a table that is (columns)
id INT PK
name VARCHAR
date DATE
I have a grouping select
select
name,
max(date)
from table
group by name
that gives me a name and the latest date.
What is the easiest way to join the id column to the current aggregated result set with the id value where the date was the maximum?
Let me explain what my goal is with an example:
The table is filled with the data as follows
id name date
1 david 2012-12-12
2 david 2013-12-02
3 patrick 2014-01-02
4 patrick 2012-11-11
and by my query I'd like to get the following result
id name date
2 david 2013-12-02
3 patrick 2014-01-02
Notice that all the records for name = 'david' are aggregated and the maximum date is selected. How to get the row id for this maximum date?
One option is to use ROW_NUMBER():
SELECT id, name, date
FROM (
SELECT id, name, date,
row_number() over (partition by name order by date desc) rn
FROM yourtable
) t
WHERE rn = 1
SQL Fiddle Demo
Another option is to join the table back to itself using the MAX() aggregate. This option could potentially result in ties if multiple id/name combinations share the same max date:
SELECT t.id, t.name, t.date
FROM yourtable t
JOIN (SELECT name, max(date) maxdate
FROM yourtable
GROUP BY name) t2 on t.name = t2.name AND t.date = t2.maxdate
More Fiddle

Select ranked records whose sum adds up to a certain value

+----+--------+----------+-----------+
| ID | Number | Reason | Timestamp |
+----+--------+-----------+----------+
| 1 | 2 | Business | date |
| 2 | 3 | Pleasure | date |
+----+--------+-----------+----------+
I've got a table that looks like above. I'm trying to figure out how I can get the latest records (table ranked by timestamp) whose Number columns add up to a certain value.
So in the above example, if I had a value of 5, I would want these 2 records (assuming they were the most recent). I would like to get an output string like the following:
2 (Business), 3 (Pleasure)
Any ideas?
If you are looking for the most recent records that add up to no more than X, then you need a cumulative sum. In SQL Server 2008, this is handled using a correlated subquery:
select t.*
from (select t.*,
(select sum(number) from t t2 where t2.id <= t.id) as CumeSumValue
from t
) t
where CumeSumValue <= X
I think >= requires a bit more logic.
select t.*
from (select t.*,
(select sum(number)
from t t2
where t2.id <= t.id
) as CumeSumValue
from t
) t
where CumeSumValue <= X or -- less than or equal
((CumeSumValue - Number < X) and (CumeSumValue > X)) -- first that is greater than
CumeSumValue >= X
Have you tried using a cursor?
This looks like the prime example for using a cursor.
http://msdn.microsoft.com/de-de/library/ms180169.aspx
At first you should get latest records
Select Max(ID) MAXID Into #t From t Group By Timestamp
Now in #t all latest records available . You can join in with table to access all fields :
Select t.* from t Join #t on t.ID = #t.MAXID
If you want to filter something it could be done with simple where clause of having .

Fetch Max from a date column grouped by a particular field

I have a table similar to this:
LogId RefId Entered
==================================
1 1 2010-12-01
2 1 2010-12-04
3 2 2010-12-01
4 2 2010-12-06
5 3 2010-12-01
6 1 2010-12-10
7 3 2010-12-05
8 4 2010-12-01
Here, LogId is unique; For each RefId, there are multiple entries with timestamp. What I want to extract is LogId for each latest RefId.
I tried solutions from this link:http://stackoverflow.com/questions/121387/sql-fetch-the-row-which-has-the-max-value-for-a-column. But, it returns multiple rows with same RefId. The LogId as well as RefId should be unique.
Can someone help me with this?
Thanks
Vamyip
You need to use a subquery that extracts the latest Entered value for each RefId, and then join your source table to this on RefId, Entered:
SELECT DISTINCT MyTable.LogId, MyTable.Entered FROM MyTable
INNER JOIN (SELECT RefId, MAX(Entered) as Entered FROM MyTable GROUP BY RefId) Latest
ON MyTable.RefId = Latest.RefId AND MyTable.Entered = Latest.Entered
Since it appears auto-increment log ID, they would be date/time stamped in sequential order. So, by grabbing the last LogID per Reference ID, you'll have the "most recent" one in the "PreQuery" below, then join based on that single ID to the original table to get the actual date stamp info (or other details) you need from the actual log.
select PreQuery.RefID,
PreQuery.LastLogEntry,
L.Entered
from
( select RefID,
Max( LogID ) LastLogEntry
from
YourLog
group by
RefID ) PreQuery,
YourLog L
where
PreQuery.LastLogEntry = L.LogID
To handle the duplicates correctly:
SELECT m.*
FROM (
SELECT DISTINCT refid
FROM mytable
) md
JOIN mytable m
ON m.LogID =
(
SELECT LogID
FROM mytable mi
WHERE mi.refid = md.refid
ORDER BY
mi.refid DESC, mi.entered DESC, mi.logid DESC
LIMIT 1
)
Create an index on mytable (refid, entered, logid) for this to work fast.