What I want: I'm having problems with a greatest-n-per-group problem. My group is a set of TCP Addresses and the n is the date at which the table row was inserted into the database.
The problem: I'm currently getting all rows with tcp Addresses which match my where clause, rather then one with the largest date per tcp address.
I'm trying to follow this example and failing: SQL Select only rows with Max Value on a Column.
Here's what my table looks like.
CREATE TABLE IF NOT EXISTS `xactions` (
`id` int(15) NOT NULL AUTO_INCREMENT,
`tcpAddress` varchar(40) NOT NULL,
//a whole lot of other stuff in batween
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=150 ;
Example rows are
ID | tcpAddress | ... | date
1 | 192.168.1.161 | ... | 2012-09-12 14:19:39
2 | 192.168.1.162 | ... | 2012-09-12 14:19:40
3 | 192.168.1.162 | ... | 2012-09-12 14:19:41
4 | 192.168.1.162 | ... | 2012-09-12 14:19:42
SQL statement I'm trying to use
select yt.id, yt.tcpAddress, yt.analog, yt.discrete, yt.counter, yt.date
from xactions yt
inner join(
select id, tcpAddress, analog, discrete, counter, max(date) date
from xactions
WHERE tcpAddress='192.168.1.161' OR tcpAddress='192.168.1.162'
group by date
) ss on yt.id = ss.id and yt.date= ss.date
You need to group by the tcpAddress, not by the date.
And join by the tcpAddress, not the id.
select yt.id, yt.tcpAddress, yt.analog, yt.discrete, yt.counter, yt.date
from xactions yt
inner join (
select tcpAddress, max(date) date
from xactions
where tcpAddress in ('192.168.1.161', '192.168.1.162')
group by tcpAddress
) ss using (tcpAddress, date);
Also, you don't need to select any extra columns in the derived table -- only the tcpAddress and the max(date).
Also you can use option with EXISTS(). In EXISTS() find MAX(date) for each group of tcpAddress
and compare them
SELECT id, tcpAddress, analog, discrete, counter, date
FROM xactions x1
WHERE EXISTS (
SELECT 1
FROM xactions x2
WHERE x1.tcpAddress = x2.tcpAddress
HAVING MAX(x2.date) = x1.date
) AND (tcpAddress='192.168.1.161' OR tcpAddress='192.168.1.162')
Related
I have this issue where I want to show only the latest record (Col 1). I deleted the date column thinking that it might not work if it has different values. but if that's the case, then the record itself has a different name (Col 1) because it has a different date in the name of it.
Is it possible to fetch one record in this case?
The code:
SELECT distinct p.ID,
max(at.Date) as date,
at.[RAPID3 Name] as COL1,
at.[DLQI Name] AS COL2,
at.[HAQ-DI Name] AS COL3,
phy.name as phyi,
at.State_ID
FROM dbo.[Assessment Tool] as at
Inner join dbo.patient as p on p.[ID] = at.[Owner (Patient)_Patient_ID]
Inner join dbo.[Physician] as phy on phy.ID = p.Physician_ID
where (at.State_ID in (162, 165,168) and p.ID = 5580)
group by
at.[RAPID3 Name],
at.[DLQI Name],
at.[HAQ-DI Name],
p.ID, phy.name,
at.State_ID
SS:
In this SS I want to show only the latest record (COL 1) of this ID "5580". Means the first row for this ID.
Thank you
The Most Accurate way to handle this.
Extract The Date.
Than use Top and Order.
create table #Temp(
ID int,
Col1 Varchar(50) null,
Col2 Varchar(50) null,
Col3 Varchar(50) null,
Phyi Varchar(50) null,
State_ID int)
Insert Into #Temp values(5580,'[9/29/2021]-[9.0]High Severity',null,null,'Eman Elshorpagy',168)
Insert Into #Temp values(5580,'[10/3/2021]-[9.3]High Severity',null,null,'Eman Elshorpagy',168)
select top 1 * from #Temp as t
order by cast((Select REPLACE((SELECT REPLACE((SELECT top 1 Value FROM STRING_SPLIT(t.Col1,'-')),'[','')),']','')) as date) desc
This is close to ANSI standard, and it also caters for the newest row per id.
The principle is to use ROW_NUMBER() using a descending order on the date/timestamp (using a DATE type instead of a DATETIME and avoiding the keyword DATE for a column name) in one query, then to select from that query using the result of row number for the filter.
-- your input, but 2 id-s to show how it works with many ..
indata(id,dt,col1,phyi,state_id) AS (
SELECT 5580,DATE '2021-10-03','[10/3/2021] - [9,3] High Severity','Eman Elshorpagy',168
UNION ALL SELECT 5580,DATE '2021-09-29','[9/29/2021] - [9,0] High Severity','Eman Elshorpagy',168
UNION ALL SELECT 5581,DATE '2021-10-03','[10/3/2021] - [9,3] High Severity','Eman Elshorpagy',168
UNION ALL SELECT 5581,DATE '2021-09-29','[9/29/2021] - [9,0] High Severity','Eman Elshorpagy',168
)
-- real query starts here, replace following comman with "WITH" ...
,
with_rank AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY dt DESC) AS rank_id
FROM indata
)
SELECT
id
, dt
, col1
, phyi
, state_id
FROM with_rank
WHERE rank_id=1
;
id | dt | col1 | phyi | state_id
------+------------+-----------------------------------+-----------------+----------
5580 | 2021-10-03 | [10/3/2021] - [9,3] High Severity | Eman Elshorpagy | 168
5581 | 2021-10-03 | [10/3/2021] - [9,3] High Severity | Eman Elshorpagy | 168
I have following table:
DROP TABLE IF EXISTS t
CREATE TABLE t
(
id INT IDENTITY PRIMARY KEY,
dt datetime,
type int,
grp int,
typecol1 varchar(10),
typecol2 varchar(10),
typecol3 varchar(10),
typecol4 varchar(10)
)
INSERT INTO t (dt,type,grp,typecol1,typecol2,typecol3,typecol4)
VALUES
('2019-01-15',1,1,'A',null,null,null),
('2019-01-15',2,2,null,'B',null,null),
('2019-01-15',3,3,null,null,'C',null),
('2019-01-15',4,4,null,null,null,'D'),
('2019-02-15',1,1,'AA',null,null,null),
('2019-02-15',4,2,null,null,null,'DD'),
('2019-03-15',3,1,null,null,'CCC',null),
('2019-04-15',2,1,null,'BBBB',null,NULL);
In this table type will be 1,2,3,4.. here date and type both are composite key.
I need to merge the row based if same date exist to single row
and merge based on only below condition
if same date &
type=1 then merge to typecol1
type=2 then merge to typecol2
type=3 then merge to typecol3
type=4 then merge to typecol4
and grp col is based on running count of date.
Try GROUP BY
FIDDLE DEMO
SELECT dt, MAX(typecol1) typecol1, MAX(typecol2) typecol2, MAX(typecol3) typecol3,
MAX(typecol4) typecol4
FROM t
GROUP BY dt
Output
dt typecol1 typecol2 typecol3 typecol4
15/01/2019 00:00:00 A B C D
15/02/2019 00:00:00 AA DD
15/03/2019 00:00:00 CCC
15/04/2019 00:00:00 BBBB
You just need grouping by ID with MAX() aggregation for rest of the columns :
SELECT dt,MAX(typecol1) as typecol1,
MAX(typecol2) as typecol2,
MAX(typecol3) as typecol3,
MAX(typecol4) as typecol4
FROM t
GROUP BY dt
Demo
I have 2 tables, one is working pattern, another is absences.
1) Work pattern
ID | Shift Start | Shift End
123| 01-03-2017 | 02-03-2017
2) Absences
ID| Absence Start | Absence End
123| 01-03-2017 | 04-03-2017
What would be the best way, when selecting rows from work pattern, to exclude any that have a date marked as an absence in the absence table?
For example, I have a report that uses the work pattern table to count how may days a week an employee has worked, however I don't want it to include the days that have been marked as an absence on the absence table if that makes sense? Also don't want it to include any days that fall between the absence start and absence end date?
If the span of the absence should always encompass the shift to be excluded you can use not exists():
select *
from WorkPatterns w
where not exists (
select 1
from Absences a
where a.Id = w.Id
and a.AbsenceStart <= w.ShiftStart
and a.AbsenceEnd >= w.ShiftEnd
)
rextester demo: http://rextester.com/DCODC76816
returns:
+-----+------------+------------+
| id | ShiftStart | ShiftEnd |
+-----+------------+------------+
| 123 | 2017-02-27 | 2017-02-28 |
| 123 | 2017-03-05 | 2017-03-06 |
+-----+------------+------------+
given this test setup:
create table WorkPatterns ([id] int, [ShiftStart] datetime, [ShiftEnd] datetime) ;
insert into WorkPatterns ([id], [ShiftStart], [ShiftEnd]) values
(123, '20170227', '20170228')
,(123, '20170301', '20170302')
,(123, '20170303', '20170304')
,(123, '20170305', '20170306')
;
create table Absences ([id] int, [AbsenceStart] datetime, [AbsenceEnd] datetime) ;
insert into Absences ([id], [AbsenceStart], [AbsenceEnd]) values
(123, '20170301', '20170304');
What would be the best way, when selecting rows from work pattern
If you dealing only whit dates (no time) and have control over db schema,
One approach will be to create calendar table ,
Where you going to put all dates since company started and some years in future
Fill that table once.
After it is easy to join other tables whit dates and do math.
If you have trouble whit constructing TSQL query please edit question whit more details about columns and values of tables, relations and needed results.
How about this:
SELECT WP_START.[id], WP_START.[shift_start], WP_START.[shift_end]
FROM work_pattern AS WP_START
INNER JOIN absences AS A ON WP_START.id = A.id
WHERE WP_START.[shift_start] NOT BETWEEN A.[absence_start] AND A.[absence_end]
UNION
SELECT WP_END.[id], WP_END.[shift_start], WP_END.[shift_end]
FROM work_pattern AS WP_END
INNER JOIN absences AS A ON WP_END.id = A.id
WHERE WP_END.[shift_end] NOT BETWEEN A.[absence_start] AND A.[absence_end]
See it on SQL Fiddle: http://sqlfiddle.com/#!6/49ae6/6
Here is my example that includes a Date Dimension table. If your DBAs won't add it, you can create #dateDim as a temp table, like I've done with SQLFiddle (didn't know I could do that). A typical date dimension would have a lot more details you need about the days, but if the table can't be added, just use what you need. You'll have to populate the other Holidays you need. The DateDim I use often is at https://github.com/shawnoden/SQL_Stuff/blob/master/sql_CreateDateDimension.sql
SQL Fiddle
MS SQL Server 2014 Schema Setup:
/* Tables for your test data. */
CREATE TABLE WorkPatterns ( id int, ShiftStart date, ShiftEnd date ) ;
INSERT INTO WorkPatterns ( id, ShiftStart, ShiftEnd )
VALUES
(123, '20170101', '20171031')
, (124, '20170601', '20170831')
;
CREATE TABLE Absences ( id int, AbsenceStart date, AbsenceEnd date ) ;
INSERT INTO Absences ( id, AbsenceStart, AbsenceEnd )
VALUES
( 123, '20170123', '20170127' )
, ( 123, '20170710', '20170831' )
, ( 124, '20170801', '20170820' )
;
/* ******** MAKE SIMPLE CALENDAR TABLE ******** */
CREATE TABLE dateDim (
theDate DATE NOT NULL
, IsWeekend BIT DEFAULT 0
, IsHoliday BIT DEFAULT 0
, IsWorkDay BIT DEFAULT 0
);
/* Populate basic details of dates. */
INSERT dateDim(theDate, IsWeekend, IsHoliday)
SELECT d
, CONVERT(BIT, CASE WHEN DATEPART(dw,d) IN (1,7) THEN 1 ELSE 0 END)
, CONVERT(BIT, CASE WHEN d = '20170704' THEN 1 ELSE 0 END) /* 4th of July. */
FROM (
SELECT d = DATEADD(DAY, rn - 1, '20170101')
FROM
(
SELECT TOP (DATEDIFF(DAY, '20170101', '20171231'))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id]
) AS x
) AS y ;
/* If not a weekend or holiday, it's a WorkDay. */
UPDATE dateDim
SET IsWorkDay = CASE WHEN IsWeekend = 0 AND IsHoliday = 0 THEN 1 ELSE 0 END
;
Query For Calculation:
SELECT wp.ID, COUNT(d.theDate) AS workDayCount
FROM WorkPatterns wp
INNER JOIN dateDim d ON d.theDate BETWEEN wp.ShiftStart AND wp.ShiftEnd
AND d.IsWorkDay = 1
LEFT OUTER JOIN Absences a ON d.theDate BETWEEN a.AbsenceStart AND a.AbsenceEnd
AND wp.ID = a.ID
WHERE a.ID IS NULL
GROUP BY wp.ID
ORDER BY wp.ID
Results:
| ID | workDayCount |
|-----|--------------|
| 123 | 172 | << 216 total days, 44 non-working
| 124 | 51 | << 65 total days, 14 non-working
I'm working on an Ingres DB with a script I've inherited from someone else. I need to change the script to pull out the action_times of the latest start_time and end_time event, and also the difference between the two. A sample of the DB is listed below
id_num | version | action_id | action_time
----------------------------------------------------------------------------
1 2 start_time 2014-05-26 14:58:14
1 2 end_time 2014-05-26 14:58:16
1 4 start_time 2014-05-27 10:10:57
1 4 end_time 2014-05-27 10:10:11
So far what I've come up with is:
SELECT max(a.action_time) as BIG, max(b.action_time) as SMALL, max(a.action_time) - max(b.action_time) as DIFF
FROM table1 as a, table1 as b,
WHERE a.id_num = '1' AND a.action_id = 'end_time' AND b.id_num = '1' AND b.action_id = 'start_time'
but the results are coming out as follows:
BIG SMALL DIFF
----------------------------------------------------------------------------
2014-05-27 10:10:11 2014-05-27 10:10:57 null
Apologies if a question like this has already been answered (I'm sure it probably has) but I've spent a couple of days looking over various forums and I can't find a similar example, probably how I'm phrasing the search terms. Any help would be much appreciated, I'm pretty sure I would have covered something like this in college but that was a few years ago and my SQL is a bit rusty these days. Thanks in advance!
Edit: So after some research I have come up with the following which will work in the DB GUI:
SELECT ingresdate(varchar(max(a.action_time))) as BIG, ingresdate(varchar(max(b.action_time))) as SMALL, date_part('secs',ingresdate(varchar(max(a.action_time))) - ingresdate(varchar(max(b.action_time)))) as DIFF
FROM table1 as a, table1 as b,
WHERE a.id_num = '1' AND a.action_id = 'end_time' AND b.id_num = '1' AND b.action_id = 'start_time'
If you want to calculate the difference between max(a.acction_time), and max(b.acction_time) you should use the following script:
SELECT max(a.acction_time) as BIG, max(b.acction_time) as SMALL,DATEDIFF(s, max(a.acction_time), max(b.acction_time)) as DIFF
FROM table1 as a, table1 as b
WHERE a.id_num = '1' AND a.action_id = 'end_time' AND b.id_num = '1' AND b.action_id = 'start_time'
If you do not remember DATEDIFF() function i will explain it for you.
P.S: where is the Primary key in your table1?!!
I would use sub-selects for this. Try :-
select a.action_time as max_end_time, b.action_time as max_start_time,
a.action_time - b.action_time as diff
from table a, table b
where a.action_time = (select max(action_time)
from table where action_id = 'end_time')
and b.action_time = (select max(action_time)
from table where action_id = 'start_time)
Here is my attempt:
SELECT start.action_time, end.action_time,
interval('seconds', end.action_time - start.action_time ) as diff_secs
FROM
(
SELECT action_time
FROM table a
INNER JOIN
( SELECT max(id_num) as max_id_num, max(version) as max_version FROM table
) b on ( id_num = max_id_num and version = max_version )
WHERE a.action_id = 'start_time'
) start
CROSS JOIN
(
SELECT action_time
FROM table a
INNER JOIN
( SELECT max(id_num) as max_id_num, max(version) as max_version FROM table
) b on ( id_num = max_id_num and version = max_version )
WHERE a.action_id = 'end_time'
) end
Using your data I get the following output:
+----------------------+----------------------+-----------+
| action_time | action_time | diff_secs |
+----------------------+----------------------+-----------+
| 27-May-2014 10:10:57 | 27-May-2014 10:10:11 | -46 |
+----------------------+----------------------+-----------+
For reference, here is the script I used to create and populate the test table
CREATE TABLE table
(
id_num integer,
version integer,
action_id char(10),
action_time timestamp
)
INSERT INTO table VALUES (1,2,'start_time', '2014-05-26 14:58:14');
INSERT INTO table VALUES (1,2,'end_time', '2014-05-26 14:58:16');
INSERT INTO table VALUES (1,4,'start_time', '2014-05-27 10:10:57');
INSERT INTO table VALUES (1,4,'end_time', '2014-05-27 10:10:11');
I need to make a graph from a log. The log entries are not in regular intervals.
I would like to select rows between dates along with what the values were immediately before the start date (that is, from whenever the immediatly preceeding log was entered).
So, let's say:
table Foo has id and value columns,
table Bar has id, foo_id, and value columns, and
table BarLog has id, foo_id, bar_id, bar_value and timestamp.
So there can be many Bars for one Foo.
I need all rows from BarLog for all Bars given some foo_id between, say, 07/01/2012 and 07/31/2012 and the value (row) for each Bar as it was on 07/01/2012.
Hope that made sense, if not, I'll try to clarify.
EDIT (above left for context):
Let's simplify this down another step. If I have a table with two foreign keys, fk_a and fk_b, and a timestamp, how can I get the most recent rows with a given fk_a and a distict fk_b.
As suggested, here's an example.
+----+------+------+-------------+
| id | fk_a | fk_b | timestamp |
+----+------+------+-------------+
| 1 | 1 | 1 | 01-JUL-2012 |
| 2 | 2 | 2 | 02-JUL-2012 |
| 3 | 1 | 1 | 04-JUL-2012 |
| 4 | 2 | 2 | 05-JUL-2012 |
| 5 | 1 | 3 | 07-JUL-2012 |
+----+------+------+-------------+
Given a fk_a of 1, I would want rows 3 and 5. So looking only at rows 1, 3, and 5 (those with fk_a of 1), get the most recent of each fk_b (where row 3 is more recent than row 1 for fk_b=1).
Thanks again.
Are you looking for something like this?
SELECT bl.bar_value, timestamp
FROM foo f, bar b, barlog bl
WHERE f.id = b.id
AND b.foo_id = bl.foo_id
AND timestamp BETWEEN '01-JUL-2012' AND '31-JUL-2012'
AND b.foo_id = :enter_value_here
ORDER BY timestamp DESC
Use the :enter_value_here to add the foo_id you need the data for...
What plotting tool are you using? You can take the data-set and push it into excel for plotting..in any case, hopefully the query above can get you closer to what you're trying to do.
For a dense set, create a date table and run the following query:
DECLARE #StartDate datetime
SET #StartDate = '2012-01-01'
SELECT f.ID as foo_id, b.bar_id, f.Value, GetDate() as DateStamp
FROM Foo f
inner join Bar b on f.id = b.foo_id
WHERE /*enter criteria for bar selection*/
UNION ALL
SELECT f.ID as foo_id, b.bar_id, f.Value, GetDate() as DateStamp
FROM (
SELECT MAX(bl.timestamp) as bl_timestamp, bl.bar_id as bar_id
FROM Dates d
INNER JOIN BarLog bl on bl.timestamp < d.Date
WHERE /*enter criteria for bar selection*/
GROUP BY bl.bar_id
) as pi
INNER JOIN BarLog bl on pi.bar_id = bl.bar_id and bl.timestamp = pi.bl_timestamp
WHERE d.Day_Of_Month = 1 and d.Date between #StartDate and getDate()
AND /*enter criteria for bar selection*/
The date table can be something like http://it.toolbox.com/wiki/index.php/Create_a_Time_Dimension_/_Date_Table or could be created temporarily each query by:
CREATE TABLE #Dates ([Date] datetime, Day_Of_Month int)
DECLARE #cDate datetime
SET #cDate = #StartDate
WHILE #cDate < getdate()
BEGIN
INSERT INTO #Dates (Date, Day_Of_Month)
SELECT #cDate, Datepart(d, #cdate)
SET #cDate = DATEADD(m, 1 + DATEDIFF(m, 0, #cdate), 0)
END
with a DROP TABLE #Dates sitting after the select.
This query will return:
Foo_ID, Bar_ID, Value at datestamp, Datestamp
with the datestamps incrementing by 1 month at a time.
Finally found this question which had what I was looking for. Basically just joining with a grouped select. So the answer for my edit would be something like
SELECT * FROM SomeTable a
JOIN (
SELECT fk_b, MAX(timestamp) as latest
FROM SomeTable
GROUP BY fk_b
) b
ON a.id = b.id
WHERE a.fk_a = #someIdA
Which would return the latest of each distinct fk_b with a specified fk_a
The original question would just be a union of this with a simple get between dates