How to show only the latest record in SQL - sql

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

Related

Compare two rows (both with different ID) & check if their column values are exactly the same. All rows & columns are in the same table

I have a table named "ROSTER" and in this table I have 22 columns.
I want to query and compare any 2 rows of that particular table with the purpose to check if each column's values of that 2 rows are exactly the same. ID column always has different values in each row so I will not include ID column for the comparing. I will just use it to refer to what rows will be used for the comparison.
If all column values are the same: Either just display nothing (I prefer this one) or just return the 2 rows as it is.
If there are some column values not the same: Either display those column names only or display both the column name and its value (I prefer this one).
Example:
ROSTER Table:
ID
NAME
TIME
1
N1
0900
2
N1
0801
Output:
ID
TIME
1
0900
2
0801
OR
Display "TIME"
Note: Actually I'm okay with whatever result or way of output as long as I can know in any way that the 2 rows are not the same.
What are the possible ways to do this in SQL Server?
I am using Microsoft SQL Server Management Studio 18, Microsoft SQL Server 2019-15.0.2080.9
Please try the following solution based on the ideas of John Cappelletti. All credit goes to him.
SQL
-- DDL and sample data population, start
DECLARE #roster TABLE (ID INT PRIMARY KEY, NAME VARCHAR(10), TIME CHAR(4));
INSERT INTO #roster (ID, NAME, TIME) VALUES
(1,'N1','0900'),
(2,'N1','0801')
-- DDL and sample data population, end
DECLARE #source INT = 1
, #target INT = 2;
SELECT id AS source_id, #target AS target_id
,[key] AS [column]
,source_Value = MAX( CASE WHEN Src=1 THEN Value END)
,target_Value = MAX( CASE WHEN Src=2 THEN Value END)
FROM (
SELECT Src=1
,id
,B.*
FROM #roster AS A
CROSS APPLY ( SELECT [Key]
,Value
FROM OpenJson( (SELECT A.* For JSON Path,Without_Array_Wrapper,INCLUDE_NULL_VALUES))
) AS B
WHERE id=#source
UNION ALL
SELECT Src=2
,id = #source
,B.*
FROM #roster AS A
CROSS APPLY ( SELECT [Key]
,Value
FROM OpenJson( (SELECT A.* For JSON Path,Without_Array_Wrapper,INCLUDE_NULL_VALUES))
) AS B
WHERE id=#target
) AS A
GROUP BY id, [key]
HAVING MAX(CASE WHEN Src=1 THEN Value END)
<> MAX(CASE WHEN Src=2 THEN Value END)
AND [key] <> 'ID' -- exclude this PK column
ORDER BY id, [key];
Output
+-----------+-----------+--------+--------------+--------------+
| source_id | target_id | column | source_Value | target_Value |
+-----------+-----------+--------+--------------+--------------+
| 1 | 2 | TIME | 0900 | 0801 |
+-----------+-----------+--------+--------------+--------------+
A general approach here might be to just aggregate over the entire table and report the state of the counts:
SELECT
CASE WHEN COUNT(DISTINCT ID) = COUNT(*) THEN 'Yes' ELSE 'No' END AS [ID same],
CASE WHEN COUNT(DISTINCT NAME) = COUNT(*) THEN 'Yes' ELSE 'No' END AS [NAME same],
CASE WHEN COUNT(DISTINCT TIME) = COUNT(*) THEN 'Yes' ELSE 'No' END AS [TIME same]
FROM yourTable;

2 sql queries 2 different results using UNION

I have 2 queries that start with same tables but filter different columns. Both queries are unioned together so I can get a single count of people without duplication.
If I run the queries with the union commented out I have the same number of rows in each 1,953. When I run with the union I get 1,816 in one and 1,922 in the other.
My data is just an account # like 123456 in the first column and a 1/0 in the second column. Help me understand how this can happen if I am starting with the same number of rows.
Here is one of the queries
select distinct acct#,
case
when (lastFilledDate is not null and lastFilledDate<>'00/00/00') or
([Last Filled DC] is not null and [Last Filled DC]<>'00/00/00') or
(vivitrol is not null and vivitrol <>'00/00/00') or
(sublocade is not null and sublocade <>'00/00/00') or
(naltrexone is not null and naltrexone <>'00/00/00') then 1
else 0 end as result
from
(
select Acct#, DOB, [COE Contact Note], [COE-INTAKA Doc], [COE-MOM
Doc], lastFilledDate, [Last Filled DC],vivitrol,sublocade,naltrexone,
ROW_NUMBER() over (partition by Acct# order by [COE-INTAKA Doc] desc)
as apptRows
from tblAppBSCImportDashCOE2279 as main
where (([COE-MOM Doc]='Yes' and [COE Contact Note] is not null) or
[COE-MOM Doc]='No') and Appt is not null
) as sub
where apptRows=1
union
select distinct acctNo,
case
when
providerMAT='The Wright Center' and [COE-MOM Doc] is not null then
1
else 0
end as result
from
(
select acctNo, [COE-MOM Doc], MAT, providerMAT,
ROW_NUMBER() over (partition by acctNo order by COEBNMOM, [COE-MOM Doc]
desc) as apptRows
from tblAppBSCImportDashCOEHM2544 as main
where [COE-MOM Doc] is not null or COEBNMOM is not null
) as sub
where apptRows=1
results look like
acct# result
123456 1
234567 0
There is one possibility. The records you selected may result in duplicate records WITHIN each select statement. Let me try to illustrate with an example. (you can input the following query into your session to follow along)
IF OBJECT_ID('TEMPDB..#TEMP1') IS NOT NULL
DROP TABLE #TEMP1
IF OBJECT_ID('TEMPDB..#TEMP2') IS NOT NULL
DROP TABLE #TEMP2
CREATE TABLE #TEMP1(
id INT
,account INT
,amount INT
,yes_no INT
)
INSERT INTO #TEMP1 (id,account,amount,yes_no)
VALUES(1,123456,5,0)
,(2,123456,10,0)
,(3,123456,20,0)
CREATE TABLE #TEMP2(
id INT
,account INT
,amount INT
,yes_no INT
)
INSERT INTO #TEMP2 (id,account,amount,yes_no)
VALUES(4,123456,5,0)
,(5,123456,10,0)
,(6,123456,20,0)
SELECT *
FROM #TEMP1
SELECT *
FROM #TEMP2
Output of this is 2 tables with distinct records:
Now suppose I write queries that select account and the 'yes_no' column:
SELECT account,yes_no
FROM #TEMP1
SELECT account,yes_no
FROM #TEMP2
You can see that now all of the records are the same values within each select statement. So what do you think happens when I union these queries together?
SELECT account,yes_no
FROM #TEMP1
UNION
SELECT account,yes_no
FROM #TEMP2
UNION will output the distinct values of the ENTIRE OUTPUT, which also applies within each query. This is an extreme example of what I think you are experiencing. You need to include some sort of ID for each query such that it can be distinguished from other records within the query, like;
SELECT id,account,yes_no
FROM #TEMP1
UNION
SELECT id,account,yes_no
FROM #TEMP2

Rotate rows into columns with column names not coming from the row

I've looked at some answers but none of them seem to be applicable to me.
Basically I have this result set:
RowNo | Id | OrderNo |
1 101 1
2 101 10
I just want to convert this to
| Id | OrderNo_0 | OrderNo_1 |
101 1 10
I know I should probably use PIVOT. But the syntax is just not clear to me.
The order numbers are always two. To make things clearer
And if you want to use PIVOT then the following works with the data provided:
declare #Orders table (RowNo int, Id int, OrderNo int)
insert into #Orders (RowNo, Id, OrderNo)
select 1, 101, 1 union all select 2, 101, 10
select Id, [1] OrderNo_0, [2] OrderNo_1
from (
select RowNo, Id, OrderNo
from #Orders
) SourceTable
pivot (
sum(OrderNo)
for RowNo in ([1],[2])
) as PivotTable
Reference: https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017
Note: To build each row in the result set the pivot function is grouping by the columns not begin pivoted. Therefore you need an aggregate function on the column that is being pivoted. You won't notice it in this instance because you have unique rows to start with - but if you had multiple rows with the RowNo and Id you would then find the aggregation comes into play.
As you say there are only ever two order numbers per ID, you could join the results set to itself on the ID column. For the purposes of the example below, I'm assuming your results set is merely selecting from a single Orders table, but it should be easy enough to replace this with your existing query.
SELECT o1.ID, o1.OrderNo AS [OrderNo_0], o2.OrderNo AS [OrderNo_1]
FROM Orders AS o1
INNER JOIN Orders AS o2
ON (o1.ID = o2.ID AND o1.OrderNo <> o2.OrderNo)
From your sample data, simplest you can try to use min and MAX function.
SELECT Id,min(OrderNo) OrderNo_0,MAX(OrderNo) OrderNo_1
FROM T
GROUP BY Id

Find the difference between top 2 rows

I am trying to workout the difference between the top 2 rows for all columns in a table. I will just specify one column to make this easier. I am new to SQL writing so apologies if this is easy. I am on SSMS and so far i think i need to inner join the table then compare row 1 of table1 with row 2 of table2. The id column works in that the newest row is the highest id. I need to take the highest id and then the second highest id (row 2) and find the difference.
SELECT Table1.id,
Table1.transferdate,
Table1.payment,
Table2.id,
Table2.transferdate,
Table2.payment
FROM Table1 AS Table1
INNER JOIN Table2 AS Table2 ON Table1.id = Table2.id
I want to see the difference between yesterday (Top row) and previous day to that (second row) in the payment column. The payment column should ever increase as that data is added on to the previous days. I'm just not sure where to go with it after the INNER JOIN and nothing I have tried has worked.
Data example of what i currently have:
id | transferdate |payment | debt | mailing_batch
46 | 2017-05-18 |651681 | 616816 | 1861651
45 | 2017-05-17 |601680 | 516168 | 1616866
What i want is the difference:
id | transferdate |payment | debt | mailing_batch
1 | 1 |50001 | 100648 | 244785
I only ever want to see the difference between the top 2 for each column. Would i just delete out the select column names and just leave the ones that i have used the LEAD function on with TOP1?
Not interested in any other of the rows. Just always the top 2 as this is a data copy table and its a way to ensure the data has updated correctly in the business set up.
SELECT Top 1 Table1.id,
Table1.payment-LEAD(Table1.payment,1) over(order by Table1.id desc)
FROM Table1 AS Table1
INNER JOIN Table2 AS Table2 ON Table1.id = Table2.id
order by Table1.id desc
If you are using a version of sql server 2012 or higher, you can use the above query.
;With CTE(id , transferdate ,payment , debt , mailing_batch)
AS
(
SELECT 46 , '2017-05-18' ,651681 , 616816 , 1861651 UNION ALL
SELECT 45 , '2017-05-17' ,601680 , 516168 , 1616866
)
SELECT Id
,transferdate
,payment
,debt
,mailing_batch
FROM (
SELECT (LEadId - Id) AS Id
,DATEDIFF(DAY, transferdate, Leadtransferdate) AS transferdate
,(Leadpayment - payment) AS payment
,(Leaddebt - debt) AS debt
,(Leadmailing_batch - mailing_batch) AS mailing_batch
,ROW_NUMBER() OVER (
ORDER BY (
SELECT 1
)
) AS Seq
FROM (
SELECT *
,LEAD(id) OVER (
ORDER BY id
) AS LEadId
,LEAD(transferdate) OVER (
ORDER BY transferdate
) AS Leadtransferdate
,LEAD(payment) OVER (
ORDER BY payment
) AS Leadpayment
,LEAD(debt) OVER (
ORDER BY debt
) AS Leaddebt
,LEAD(mailing_batch) OVER (
ORDER BY mailing_batch
) AS Leadmailing_batch
FROM CTE
) Dt
) Final
WHERE Final.Seq = 1
OutPut
id | transferdate |payment | debt | mailing_batch
1 | 1 |50001 | 100648 | 244785
Which version of sql server you're using ? If it is 2012+ then LEAD and LAG functions will work. You can read more about them here and here
Try it yourself as they're really easy. Show some efforts and if not working, I will help you out.
update after comments
CREATE TABLE [dbo].[t1_new](
[Id] [int] NULL,
[transferdate] [date] NULL,
[payment] [int] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[t1_new] ([Id], [transferdate], [payment]) VALUES (1, CAST(N'2017-05-18' AS Date), 1000)
GO
INSERT [dbo].[t1_new] ([Id], [transferdate], [payment]) VALUES (2, CAST(N'2017-05-19' AS Date), 1100)
GO
INSERT [dbo].[t1_new] ([Id], [transferdate], [payment]) VALUES (3, CAST(N'2017-05-20' AS Date), 1200)
GO
INSERT [dbo].[t1_new] ([Id], [transferdate], [payment]) VALUES (4, CAST(N'2017-05-21' AS Date), 1400)
GO
select top 1 t1.*,
LAG(transferdate) OVER (ORDER BY transferdate) previous_date,
LAG(payment) OVER (ORDER BY payment) previou_payment,
t1.payment - (LAG(payment) OVER (ORDER BY payment)) as payment_difference
from t1_new t1
order by Id desc
screenshots :
input table
output table
PS: you don't need 2 separate tables. Your issue can be resolved by 1 table itself.

Rows without repeating records

id | name
-------+-------------------------------------
209096 | Pharmacy
204200 | Eyecare Center
185718 | Duffy PC
214519 | Shopko
162225 | Edward Jones
7609 | Back In Action Chiropractic Center
I use select id, name from customer order by random()
There are 6 records i just want that when ever i query, i will get a unique row each time for six times and then it starts again from first or the records are ordered each time that the top one did't repeat
This will give you 6 random rows each time. The Group By is to ensure unique rows if your id is not a unique primary key, so maybe not needed - depending on your table structure.
SELECT TOP 6 id, name, ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]
FROM customer
GROUP BY id,name
ORDER BY [RandomNumber]
Edit: Sorry! Didn't read the question properly. Although you can use this to get a random row each time :)
If you want to specifically get all 6 rows in a random order 1 at a time you will need to store the order somewhere. Suggest creating a temp table and selecting from there, or if you are using a front end webpage get all 6 rows and store in a dataset.
You can use that logic,
"milisecond" part of current date is always changing. We have a id column as numeric. So we can use modular function to get randomized order:
create table #data(id numeric(10), name varchar(20))
insert #data select 209096 , 'Pharmacy'
insert #data select 204200 , 'Eyecare Center'
insert #data select 185718 , 'Duffy PC'
insert #data select 214519 , 'Shopko'
insert #data select 162225 , 'Edward Jones'
insert #data select 7609 , 'Back In Action Chiropractic Center'
select * from #data order by id % (datepart(ms, getdate()))
OK Maybe there is another way to do it just in SQL. Add a new BIT column "selected". Definitely not the fastest/best performance way to do it.
DECLARE #id INT
IF NOT EXISTS (SELECT TOP 1 id FROM customer WHERE selected = 0)
BEGIN
UPDATE customer SET selected = 0
END
SELECT #id = id FROM
(SELECT TOP 1 id, ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]
FROM customer WHERE selected = 0
ORDER BY [RandomNumber]) a
UPDATE customer SET selected = 1 WHERE id = #id
SELECT id, name FROM customer WHERE id = #id