Only select most recent record (calldatetime) SQL - sql

Im trying to only select the most recent calldatetime and display that in my results, can anyone help? I need the results to only show the latest updated record
SELECT
ROW_NUMBER() OVER (PARTITION BY l.phonenum ORDER BY h.calldatetime) as rn,
h.HistoryID,
l.ProjectID,
h.ProjName,
l.CompanyName,
l.Phonenum,
l.fname,
l.lname,
l.Address1,
l.Address2,
l.Address3,
l.Town,
l.Postcode,
l.county,
h.CallDateTime,
cb.CBDatetime,
l.apptdate,
l.appttime,
h.CRC,
c.Description,
a.Firstname + ' ' + a.Lastname AS AgentName,
l.Notes
FROM
History h inner join
cmp_UtilityTrade l on h.PhoneNum = l.Phonenum left outer join
CRC c on c.CRC = h.CRC left outer join
Agent a on a.AgentID = h.AgentID left outer join
CallBack cb on cb.DialID = h.DialID
WHERE
h.calldatetime BETWEEN '2016-05-01 00:00:00' AND '2016-06-15 23:00:00'
ORDER BY
l.Phonenum

It looks like you already have the row_number() over window function setup the way you need it. Assuming it's returning the correct value, you simply need to filter your results by that rn value. Just place your existing query in a CTE, and filter the results like so:
with cte as (<your_query_without_order_by_phonenum>)
select *
from cte
where rn = 1
order by phonenum
Also note that you probably want to move the order by l.Phonenum clause outside the CTE, and place it in the outer query.
EDIT
As noted in the comments, you may also have to fix the order by clause inside the row_number() window function to h.calldatetime DESC.

Related

Remove multiple rows with same ID

So I've done some looking around and wasn't unable to find quite what I was looking for. I have two tables.
1.) Table where general user information is stored
2.) Where a status is generated and stored.
The problem is, is that there are multiple rows for the same users and querying these results in multiple returns. I can't just merge them because they aren't all the same status. I need just the newest status from that table.
Example of the table:
SELECT DISTINCT
TOP(50) cam.UserID AS PatientID,
mppi.DisplayName AS Surgeon,
ISNULL(sci.IOPStatus, 'N/A') AS Status,
tkstat.TrackerStatusID AS Stat_2
FROM
Main AS cam
INNER JOIN
Providers AS rap
ON cam.VisitID = rap.VisitID
INNER JOIN
ProviderInfo AS mppi
ON rap.UnvUserID = mppi.UnvUserID
LEFT OUTER JOIN
Inop AS sci
ON cam.CwsID = sci.CwsID
LEFT OUTER JOIN
TrackerStatus AS tkstat
ON cam.CwsID = tkstat.CwsID
WHERE
(
cam.Location_ID IN
(
'SURG'
)
)
AND
(
rap.IsAttending = 'Y'
)
AND
(
cam.DateTime BETWEEN CONCAT(CAST(GETDATE() AS DATE), ' 00:00:00') AND CONCAT(CAST(GETDATE() AS DATE), ' 23:59:59')
)
AND
(
cam.Status_StatusID != 'Cancelled'
)
ORDER BY
cam.UserID ASC
So I need to grab only the newest Stat_2 from each ID so they aren't returning multiple rows. Each Stat_2 also has an update time meaning I can sort by the time/date that column is : StatusDateTime
One way to handle this is to create a calculated row_number for the table where you need the newest record.
Easiest way to do that is to change your TKSTAT join to a derived table with the row_number calculation and then add a constraint to your join where the RN =1
SELECT DISTINCT TOP (50)
cam.UserID AS PatientID, mppi.DisplayName AS Surgeon, ISNULL(sci.IOPStatus, 'N/A') AS Status, tkstat.TrackerStatusID AS Stat_2
FROM Main AS cam
INNER JOIN Providers AS rap ON cam.VisitID = rap.VisitID
INNER JOIN ProviderInfo AS mppi ON rap.UnvUserID = mppi.UnvUserID
LEFT OUTER JOIN Inop AS sci ON cam.CwsID = sci.CwsID
LEFT OUTER JOIN (SELECT tk.CwsID, tk.TrackerStatusId, ROW_NUMBER() OVER (PARTITION BY tk.cwsId ORDER BY tk.CreationDate DESC) AS rn FROM TrackerStatus tk)AS tkstat ON cam.CwsID = tkstat.CwsID
AND tkstat.rn = 1
WHERE (cam.Location_ID IN ( 'SURG' )) AND (rap.IsAttending = 'Y')
AND (cam.DateTime BETWEEN CONCAT(CAST(GETDATE() AS DATE), ' 00:00:00') AND CONCAT(CAST(GETDATE() AS DATE), ' 23:59:59'))
AND (cam.Status_StatusID != 'Cancelled')
ORDER BY cam.UserID ASC;
Note you need a way to derive what the "newest" status is; I assume there is a created_date or something; you'll need to enter the correct colum name
ROW_NUMBER() OVER (PARTITION BY tk.cwsId ORDER BY tk.CreationDate DESC) AS rn
SQL Server doesn't offer a FIRST function, but you can reproduce the functionality with ROW_NUMBER() like this:
With Qry1 (
Select <other columns>,
ROW_NUMBER() OVER(
PARTITION BY <group by columns>
ORDER BY <time stamp column*> DESC
) As Seq
From <the rest of your select statement>
)
Select *
From Qry1
Where Seq = 1
* for the "newest" record.

New SQL user - I'm trying to extract the latest record in my query. Accountant new to SQL

For every AbsenceBalance.AbsenceTypesUID I want to return the latest record AbsenceBalance.BalanceTime for each AbsenceBalance.EmployeeUID
I have tried select max but it only returns the most recent entry for the entire table and not by AbsenceBalance.AbsenceTypesUID or AbsenceBalance.EmployeeUID
This is my query
SELECT TOP (1000)
AbsenceBalance.[UID],
AbsenceBalance.BalanceTime,
AbsenceBalance.AbsenceTypesUID,
AbsenceBalance.Mins,
Employee.FullName,
Employee.FirstName,
Employee.LastName,
AbsenceBalance.EmployeeUID,
absencetypes.LongName
from [RiteqDB].[dbo].[AbsenceBalance]
LEFT JOIN [RiteqDB].[dbo].Employee on AbsenceBalance.EmployeeUID = Employee.UID
LEFT JOIN [RiteqDB].[dbo].AbsenceTypes on absencebalance.AbsenceTypesUID = absencetypes.UID
where AbsenceBalance.[UID] = (select max (AbsenceBalance.[UID]) from [RiteqDB].[dbo].[AbsenceBalance] where AbsenceBalance.AbsenceTypesUID = AbsenceBalance.AbsenceTypesUID)
--where Select Max(v) from (values (AbsenceBalance.BalanceTime)
order by FullName, AbsenceTypesUID
It sounds like you might need a group by link, then either use an inner select in a where (like you have) or use this with an inner join.
SELECT
Max(AbsenceBalance.[UID]),
AbsenceBalance.AbsenceTypesUID,
AbsenceBalance.EmployeeUID,
from [RiteqDB].[dbo].[AbsenceBalance]
GROUP BY AbsenceTypesUID, EmployeeUID
;with cte as (
SELECT TOP (1000) AB.[UID]
,AB.BalanceTime
,AB.AbsenceTypesUID
,AB.Mins
,E.FullName
,E.FirstName
,E.LastName
,AB.EmployeeUID
,AT.LongName
, ROW_NUMBER() OVER(PARTITION BY AB.[UID], AB.EmployeeUID order by AB.BalanceTime DESC) AS RUN
FROM [RiteqDB].[dbo].[AbsenceBalance] AB
LEFT JOIN [RiteqDB].[dbo].Employee E ON AB.EmployeeUID = E.UID
LEFT JOIN [RiteqDB].[dbo].AbsenceTypes AT ON AB.AbsenceTypesUID = AT.UID
)
select * from cte
where RUN = 1
In your where condition your comparing with itself that might be the issue.
select max (AbsenceBalance.[UID]) from [RiteqDB].[dbo].[AbsenceBalance]
where AbsenceBalance.AbsenceTypesUID = AbsenceBalance.AbsenceTypesUID
following is wrong
AbsenceBalance.AbsenceTypesUID = AbsenceBalance.AbsenceTypesUID

access - row_number function?

I had this query, which gives me the desired results on postgres
SELECT
t.*,
ROW_NUMBER() OVER (PARTITION BY t."Internal_reference", t."Movement_date" ORDER BY t."Movement_date") AS "cnt"
FROM (SELECT
"Internal_reference",
MAX("Movement_date") AS maxtime
FROM dw."LO-D4_Movements"
GROUP BY "Internal_reference") r
INNER JOIN dw."LO-D4_Movements" t
ON t."Movement_date" = r.maxtime
AND t."Internal_reference" = r."Internal_reference"
Issue is I have to translate the query above on Access where the analytical function does not exist ...
I used this answer to build the query below
SELECT
t."Internal_reference",
t.from_code,
t.to_code,
t."Movement_date",
t.shipment_number,
t."PO_number",
t."Quantity",
t."Movement_value",
t."Site",
t."Import_date",
COUNT(*) AS "cnt"
FROM (
SELECT "Internal_reference",
MAX("Movement_date") AS maxtime
FROM dw."LO-D4_Movements"
GROUP BY "Internal_reference") r
LEFT OUTER JOIN dw."LO-D4_Movements" t
ON t."Movement_date" = r.maxtime AND t."Internal_reference" = r."Internal_reference"
GROUP BY
t.from_code,
t.to_code,
t."Movement_date",
t.shipment_number,
t."PO_number",
t."Quantity",
t."Movement_value",
t."Site",
t."Import_date",
t."Internal_reference"
ORDER BY t.from_code
Issue is I only have 1 in the cnt column.
I tried to tweak it by removing the internal_reference (see below)
SELECT
t.from_code,
t.to_code,
t."Movement_date",
t.shipment_number,
t."PO_number",
t."Quantity",
t."Movement_value",
t."Site",
t."Import_date",
COUNT(*) AS "cnt"
FROM (
SELECT "Internal_reference",
MAX("Movement_date") AS maxtime
FROM dw."LO-D4_Movements"
GROUP BY "Internal_reference") r
LEFT OUTER JOIN dw."LO-D4_Movements" t
ON t."Movement_date" = r.maxtime AND t."Internal_reference" = r."Internal_reference"
GROUP BY
t.from_code,
t.to_code,
t."Movement_date",
t.shipment_number,
t."PO_number",
t."Quantity",
t."Movement_value",
t."Site",
t."Import_date"
ORDER BY t.from_code
However, the results are even worse. The cnt is growing but it gives me the wrong cnt
Any help are more than welcome as I'm slow losing my sanity.
Thanks
Edit: Please find the sqlfiddle
I think Gordon-Linoff's code is close to what you want, but there are some typos I couldn't correct without a rewrite, so here's my attempt
SELECT
t1.Internal_reference,
t1.Movement_date,
t1.PO_Number as Combination_Of_Columns_Which_Make_This_Unique,
t1.Other_columns,
Count(1) AS Cnt
FROM
([LO-D4_Movements] AS t1
INNER JOIN [LO-D4_Movements] AS t2 ON
t1.Internal_reference = t2.Internal_reference AND
t1.Movement_date = t2.Movement_date)
INNER JOIN (
SELECT
t3.Internal_reference,
MAX(t3.Movement_date) AS Maxtime
FROM
[LO-D4_Movements] AS t3
GROUP BY
t3.Internal_reference
) AS r ON
t1.Internal_reference = r.Internal_reference AND
t1.Movement_date = r.Maxtime
WHERE
t1.PO_Number>=t2.PO_Number
GROUP BY
t1.Internal_reference,
t1.Movement_date,t1.PO_Number,
t1.Other_columns
ORDER BY
t1.Internal_reference,
t1.Movement_date,
Count(1);
In addition to within the max(movement_date) subquery, the main table is brought in twice. One version is the one for showing in your results, the other is for counting records to generate the sequence numbers.
Gordon said you need a unique id column for each row. And that's true if by "column" you mean to include derived columns also. Also it only needs to be unique within any combination of "internal_reference" and "Movement_date".
I've assumed, perhaps wrongly, that PO_Number will suffice. If not, concatenate with that (and some delimeters) other fields which will make it unique. The where clause will need updating to compare t1 and t2 for the "Combination of Columns which make this unique".
If, there is no appropriate combination available, I'm not sure it can be done without VBA and/or temp tables as The-Gambill suggested.
This is a real pain in MS Access, as far as I know. One method is a correlated subquery, but you need a unique id column on each row:
SELECT t.*,
(SELECT COUNT(*)
FROM (SELECT "Internal_reference", MAX("Movement_date") AS maxtime
FROM dw."LO-D4_Movements"
GROUP BY "Internal_reference"
) as t2
WHERE t2."Internal_reference" AND t."Internal_reference" AND
t2."Movement_date" = t."Movement_date" AND
t2.?? <= t.??
) as cnt
FROM (SELECT "Internal_reference", MAX("Movement_date") AS maxtime
FROM dw."LO-D4_Movements"
GROUP BY "Internal_reference"
) r INNER JOIN
dw."LO-D4_Movements" t
ON t."Movement_date" = r.maxtime AND
t."Internal_reference" = r."Internal_reference";
The ?? is for the id or creation date or something to allow the counting of rows.

How select from query inside case in select

I have following query
SELECT DISTINCT v.id,
( CASE
WHEN date IS NULL THEN v.o_id
ELSE (SELECT os
FROM (SELECT o_id1 os
FROM tablet t2
WHERE t2.vso_id = v.id
ORDER BY date ASC)
WHERE rownum = 1)
END ) AS p_o,
v.o_id AS k_o
FROM tablev v
LEFT JOIN tablet t
ON v.id = t.v_id;
Here is what I need:
TableV has distinct v.id values. I need join that table with another (tableT) which has many records for same v.id and then for each distinct v.id:
select O_ID from V
if record for v.id in tableT does not exists(date is null) then
select O_ID from V (same as above)else select o_id1 from T with
minimum date
Also, aditional, is if possible to join o_id from 1. and o_id/o_id1 from2 to another table and write person's name?
I would like, instead of o_id, for each v.id to join selected o_id to another table(tableO) and select name.
So, I need these columns in result: V.id, p_o, k_o
But it would be really great if it looks like: v.id, p_o.name, k_o.name
To answer your first question, assuming that t.vso_id and t.v_id are the same thing (otherwise I might have misunderstood your question), try this query (I use another DBMS, so you might need to adjust the call to COALESCE a little):
SELECT v.id, COALESCE(t.o_id1, v.o_id), v.o_id
FROM tableV v
LEFT JOIN (
(SELECT vso_id, min(date) AS min_date FROM tableT GROUP BY vso_id) min_dates
JOIN tableT t
ON min_dates.vso_id = t.vso_id AND min_dates.min_date = t.date
) ON v.id = t.v_id;
Here's a breakdown:
(SELECT vso_id, min(date) AS min_date FROM tableT GROUP BY vso_id) AS min_dates
selects a smallest date for each vso_id.
(SELECT vso_id, min(date) AS min_date FROM tableT GROUP BY vso_id) AS min_dates
JOIN tableT t
ON min_dates.vso_id = t.vso_id AND min_dates.min_date = t.date
selects one row per vso_id, and that row will correspond to the smallest date that that vso_id has.
Finally, I join tableV to that query. COALESCE(t.o_id1, v.o_id) will return t.o_id if it is not NULL, v.o_id otherwise. You can use CASE instead, but I find COALESCE to be a better fit for this particular purpose.
To answer your second question, you can then JOIN against the table with names like this:
JOIN table_with_names twn1 ON twn1.id = COALESCE(t.o_id1, v.o_id)
JOIN table_with_names twn2 ON twn2.id = v.o_id
and project twn1.name and twn2.name.

How to find a record in another table recorded at the nearest time in a subquery aggregate

select sl.*,
(select pnd_invoiceno
from PINVDET
where PND_INVNO = sl.invno and
abs(DATEDIFF(ss, pnd_date, sl.adjustedon)) =
(select min(abs(DATEDIFF(ss, pnd_date, sl.adjustedon)))
from PINVDET
where pnd_invno = sl.invno))
from vwstocklog sl where sl.invno in (select invno from vwStockDiff)
order by sl.invno, sl.adjustedon
When I run the above query I get the error:
Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression.
I understand it's saying that the expression min(abs(DATEDIFF(ss, pnd_date, sl.adjustedon))) is the problem because it references sl.adjusted on in the min() aggregate, and it cannot do so unless it's the only column referenced in the aggregate expression. What I'm not sure about is how to go about fixing it.
What I'm attempting to do here is find the pnd_invoiceno value on the record in pinvdet with the pnd_date value nearest to sl.adjustedon for the same item (and I recognize that this has the possibility of linking to multiple records).
Any ideas on how I might adjust this query to accomplish that?
Second attempt (filter first):
With x as (
select
sl.invno,
sl.adjustedon,
p.pnd_invoiceno,
rank() over (
partition by sl.invno
order by abs(datediff(ss, p.pnd_date, sl.adjustedon))
) rk
from
vwstocklog sl
inner join
pinvdet p
on p.pnd_invno = sl.invno
Where
Exists (
Select
'x'
From
vwStockDiff sd
Where
sl.invno = sd.invno
)
)
Select
x.invno,
x.adjustedon,
x.pnd_invoiceno
From
x
Where
x.rk = 1
order by
x.invno,
x.adjustedon
First attempt:
With x as (
select
sl.invno,
sl.adjustedon,
p.pnd_invoiceno,
rank() over (
partition by sl.invno
order by abs(datediff(ss, p.pnd_date, sl.adjustedon))
) rk
from
vwStockDiff sd
inner join
vwstocklog sl
on sl.invno = sd.invno
inner join
pinvdet p
on p.pnd_invno = sl.invno
)
Select
x.invno,
x.adjustedon,
x.pnd_invoiceno
From
x
Where
x.rk = 1
order by
x.invno,
x.adjustedon
If you happen to have two times that are an equal distance away, this will return a row for both. Replace rank() with row_number() if you'd only like 1.
SQLFiddle doesn't seem to be working at the moment, so I can't test this. There's probably syntax errors.