SQL - Get a subset based on Datetime using OVER clause - sql

I am trying to write a query against the following dataset that will separate the last updated records based on a unique set of fields..
Assume for the following example the the unique set of fields is val1 + val2 + val3.
I tried using window functions to achieve this, but there seems to be another level of complexity for this example.
--DROP TABLE #demo;
-- assume there is a unique key on val1 + val2 + val3
CREATE TABLE #demo
(
id int NOT NULL,
val1 varchar(100) NOT NULL,
val2 varchar(100) NOT NULL,
val3 varchar(100) NOT NULL,
last_updated DATETIME NOT NULL,
active bit not null
);
INSERT INTO #demo VALUES
(0,'a','b','c', '20150817', 0),
(1,'a','b','c', '20150817', 0),
(2,'a','b','c', '20150817', 0),
(3,'a','b','c', '20150815', 0),
(4,'a','b','c', '20150815', 0),
(5,'d','e','f', '20150701', 0),
(6,'d','e','f', '20150630', 0),
(7,'d','e','f', '20150630', 0),
(8,'d','e','f', '20150630', 0)
-- using unique key columns, trying to get only those with the most recent date
SELECT *
, ROW_NUMBER() OVER(PARTITION BY val1, val2, val3 ORDER BY last_updated DESC) AS RowNum
FROM #demo
I am hoping to return only the highlighted records below (id: 0,1,2,5) with the query.
Is this possible? Thanks in advance.

SELECT *
FROM (
SELECT *
, DENSE_RANK() OVER(PARTITION BY val1, val2, val3
ORDER BY last_updated DESC) AS RowNum
FROM #demo
)t
WHERE RowNum = 1

select d.* from
demo d join
(
SELECT val1,val2,val3 ,
max(last_updated) as mx
FROM demo
group by val1,val2,val3) x
on x.val1 = d.val1 and x.val2 = d.val2 and x.val3 = d.val3
and x.mx = d.last_updated
This is one more approach.
Fiddle

Related

SQL query to select alternate value if a value is not found

I am trying to write a sql query such as that in where condition of my query if I search for a key that does not exist, it should take value of key null in below example.
i.e. if I use below query
select value
from myTable
where Id=1 and Key = 't'
As key 't' does not exist, it should take value on key null. So I want to write my query something like this.
select value
from myTable
where Id=1
and (if key = 't' exist then give its value else give value of key = null)
Id
Key
value
1
null
val1
1
a
val2
2
x
val3
2
y
val4
2
null
val5
3
p
val6
4
q
val7
It is not possible for there to be two rows for a given Id to have a key that is NULL, and there will always be at least one row for any Id that is either t or NULL.
One way would be to apply row numbers in order of preference:
DECLARE #id int = 1;
;WITH x AS
(
SELECT Id, [Key], [value], en = ROW_NUMBER() OVER
(ORDER BY CASE WHEN [Key] = 't' THEN 1
WHEN [Key] IS NULL THEN 2 END)
FROM dbo.table
WHERE Id = #id
AND ([Key] = 't' OR [Key] IS NULL)
)
SELECT Id, [Key], [value] FROM x WHERE rn = 1;
That allows you to apply additional rules, like what to do if there are multiple rows with NULL or no rows with either.
A simpler route if those aren't possibilities:
SELECT TOP (1) Id, [Key], [value]
FROM dbo.table
WHERE Id = #id
AND ([Key] = 't' OR [Key] IS NULL)
ORDER BY [Key] DESC; -- puts NULLs last

How to convert many rows into Columns in SQL Server?

How would you convert a field that is stored as multiple rows into columns?
I listed the code below as well. Below is an example of what is needed but it can really go up to 20 columns. Thanks!
COL1 COL2 COL3
----------------
TEST 30 NY
TEST 30 CA
TEST2 10 TN
TEST2 10 TX
I would like the output to be :
COL1 COL2 COL3 COL4
------------------------
TEST 30 NY CA
TEST2 10 TN TX
select * from (
select
ID,
Name,
STORE,
Group,
Type,
Date,
State,
row_number() over(partition by ID, state order by Date desc) as rn
from
#test
) t
where t.rn = 1
declare #Table AS TABLE
(
Col1 VARCHAR(100) ,
Col2 INT ,
Col3 VARCHAR(100)
)
INSERT #Table
( Col1, Col2, Col3 )
VALUES
( 'TEST', 30 ,'NY' ),
( 'TEST', 30 ,'CA' ),
( 'TEST2', 10 ,'TN' ),
( 'TEST2', 10 ,'TX' )
SELECT
xQ.Col1,
xQ.Col2,
MAX(CASE WHEN xQ.RowNumber = 1 THEN xQ.Col3 ELSE NULL END) AS Col3,
MAX(CASE WHEN xQ.RowNumber = 2 THEN xQ.Col3 ELSE NULL END) AS Col4
FROM
(
SELECT * , RANK() OVER(PARTITION BY T.Col1,T.Col2 ORDER BY T.Col1,T.Col2,T.Col3) AS RowNumber
FROM #Table AS T
)AS xQ
GROUP BY
xQ.Col1,
xQ.Col2
There are multiple options to convert data from rows into columns. In SQL, you can use PIVOT to transform data from rows into columns.
CREATE table #tablename
(Id int, Value varchar(10), ColumnName varchar(15);
INSERT INTO #tablename
(ID, Value, ColumnName)
VALUES
(1, ‘Lucy’, 'FirstName'),
(2, ‘James’, ‘LastName’),
(3, ‘ABCDXX’, ‘Adress’),
(4, ’New York’, ‘City’),
(5, '8572685', ‘PhoneNo’);
select FirstName, LastName, Address, City, PhoneNo
from
(
select Value, ColumnName
from #tablename
) d
pivot
(
max(Value)
for ColumnName in (FirstName, LastName, Address, City, PhoneNo)
) piv;
Refer the below link for other options of transforming data from rows to columns:
https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/

How to return only numbers from query where column is nvarchar

I have a simple query that is returning records where "column2" > 0
Here is the data in the database
Column1 Column2
1 123456789
2 123456781
3 13-151-1513
4 alsdjf
5
6 000000000
Her is the query
select column1, replace(a.Payroll_id,'-','')
from table1
where isnumeric(column2) = 1
I'd like to return the following:
Column1 Column2
1 123456789
2 123456781
3 131511513
This mean, I won't select any records when the column is blank (or null), will not return a row if it's not an integer, and will drop out the '-', and would not show row 6 since it's all 0.
How can I do this?
I think you can use something like this :
USE tempdb
GO
CREATE TABLE #Temp
(
ID INT IDENTITY
,VALUE VARCHAR(30)
)
INSERT INTO #Temp (VALUE) VALUES ('1213213'), ('1213213'), ('121-32-13'), ('ASDFASF2123')
GO
WITH CteData
AS
(
SELECT REPLACE(VALUE,'-','') as Valor FROM #Temp
)
SELECT * FROM CteData WHERE (ISNUMERIC(Valor) = 1 AND valor not like '%[0-0]%')
DROP TABLE #Temp
then you can apply validations for empty, NULL,0 etc
If you are using SQL2012 or above you can also use TRY_PARSE that is more selective in its parsing. This function will return NULL if a record can't be converted. You could use it like this:
CREATE TABLE #temp
(
ID INT IDENTITY ,
VALUE VARCHAR(30)
)
INSERT INTO #temp
( VALUE )
VALUES ( '1213213' ),
( '1213213' ),
( '121-32-13' ),
( 'ASDFASF2123' ),
( '0000000' )
SELECT ParsedValue
FROM #temp
CROSS APPLY ( SELECT TRY_PARSE(
Value AS INT ) AS ParsedValue
) details
WHERE ParsedValue IS NOT NULL
AND ParsedValue>0

SQL - Select the largest value within a row

I seem to be stuck on this and can't find a solution having had a look around.
I have an SQL table who's first row looks something like this:
Name Val1 Val2 Val3
John 1000 2000 3000
What I need to do is Select the largest value within this row i.e. 3000
Obviously if these values were in a column rather than row you could just use SELECT MAX(column) FROM table to get the largest value in the column. Is there an equivalent of this for finding the max value in a row?
I have also had a look at the uses of PIVOT and UNPIVOT but I don't think they are useful to me here..
The only way I have been able to do it is to create a temp table and insert each value into a single column like so:
CREATE TABLE #temp (colvals float)
INSERT INTO #temp (colvals)
SELECT Val1 FROM table WHERE ID=1
UNION
SELECT Val2 FROM table WHERE ID=1
UNION
SELECT Val3 FROM table WHERE ID=1
--------------------------------------------
SELECT MAX(colvals) FROM #temp
--------------------------------------------
DROP TABLE #temp
However I feel this is rather slow especially as my table has a lot more columns than the snippet I have shown above.
Any ideas?
Thanks in advance.
You can build a reference table for columns by APPLY and use native MAX()
-- Sample Data
declare #data table (Name varchar(10), Val1 int, Val2 int, Val3 int, Val4 int, Val5 int, Val6 int)
insert #data values
('John', 1000, 2000, 3000, 4000, 5000, 6000),
('Mary', 1, 2, 3, 4, 5, 6)
select Name, MaxValue from
#data
cross apply
(
select max(value) as MaxValue
from
(values
(Val1),(Val2),(Val3),(Val4),(Val5),(Val6) -- Append here
) t(value)
) result
SQL Fiddle
select MAX(case when c1 > c2 and c1 > c3 then c1
when c2 > c3 then c2
else c3
end)
from tablename
You need something like this:
SELECT *, Row_Number() OVER (ORDER BY GETDATE()) Rowid INTO #temp From yourtable
DECLARE #Columns AS Varchar(MAX)
SET #Columns =''
SELECT #Columns = #Columns + ',[' + name + ']' FROM tempdb..syscolumns
WHERE id=object_id('tempdb..#temp') AND name <> 'Rowid'
SELECT #Columns = Right(#Columns, len(#Columns)-1)
exec ('Select Rowid,Max(val) maxval from #temp t Unpivot(val For data in (' + #Columns + ')) as Upvt Group by Rid')
Drop table #temp
Use math logic:
select
case
when val1 >= val2 and val1 >= val2 then val1
when val2 >= val1 and val2 >= val3 then val2
else val3
end maxVal
from mytable
where id = 1
I think you were on the right track when you looked at unpivot as an option. Becaue that's exactly what you want to do - you have a pivot table, and you want the unpivoted value from it. Here's what I came up with:
declare #base table (Name char(4), Val1 int, Val2 int ,Val3 int);
insert into #base (Name, Val1 , Val2 , Val3) values ('John' , 1000 , 2000 , 3000);
select name, max(value) as max_value
from (
select name, valuetype, value
from #base b
unpivot ( value for valuetype in (Val1 , Val2 , Val3)) as u
) as up
group by name
To expand to your whole table, you can then just add more column names to the unpivot row:
unpivot ( value for valuetype in (Val1 , Val2 , Val3, ... more values here...)) as u
You could always replicate this answer Is there a Max function in SQL Server that takes two values like Math.Max in .NET?
-- Sample Data
declare #data table (Name varchar(10), Val1 int, Val2 int, Val3 int, Val4 int, Val5 int, Val6 int)
insert #data values
('John', 1000, 2000, 3000, 4000, 5000, 6000),
('Mary', 1, 2, 3, 4, 5, 6),
('Tony66', 1, 2, 3, 4, 5, 66),
('Tony55', 1, 2, 3, 4, 55, 6),
('Tony44', 1, 2, 3, 44, 5, 6),
('Tony33', 1, 2, 33, 4, 5, 6),
('Tony22', 1, 22, 3, 4, 5, 6),
('Tony11', 11, 2, 3, 4, 5, 6)
SELECT name,
(SELECT MAX(value)
FROM (VALUES (Val1),(Val2), (Val3), (Val4), (Val5), (Val6)) AS AllValues(value)) AS 'MaxValue'
FROM #data

SQL: Remove duplicates

How do I remove duplicates from a table that is set up in the following way?
unique_ID | worker_ID | date | type_ID
A worker can have multiple type_ID's associated with them and I want to remove any duplicate types. If there is a duplicate, I want to remove the type with the most recent entry.
A textbook candidate for the window function row_number():
;WITH x AS (
SELECT unique_ID
,row_number() OVER (PARTITION BY worker_ID,type_ID ORDER BY date) AS rn
FROM tbl
)
DELETE FROM tbl
FROM x
WHERE tbl.unique_ID = x.unique_ID
AND x.rn > 1
This also takes care of the situation where a set of dupes on (worker_ID,type_ID) shares the same date.
See the simplified demo on data.SE.
Update with simpler version
Turns out, this can be simplified: In SQL Server you can delete from the CTE directly:
;WITH x AS (
SELECT unique_ID
,row_number() OVER (PARTITION BY worker_ID,type_ID ORDER BY date) AS rn
FROM tbl
)
DELETE x
WHERE rn > 1
delete from table t
where exists ( select 1 from table t2
where t2.worker_id = t.worker_id
and t2.type_id = t.type_id
and t2.date < t.date )
HTH
DELETE FROM #t WHERE unique_Id IN
(
SELECT unique_Id FROM
(
SELECT unique_Id
,Type_Id
,ROW_NUMBER() OVER (PARTITION BY worker_Id, type_Id ORDER BY date) AS rn
FROM #t
) Q
WHERE rn > 1
)
And to test...
DECLARE #t TABLE
(
unique_ID INT IDENTITY,
worker_ID INT,
date DATETIME,
type_ID INT
)
INSERT INTO #t VALUES (1, DATEADD(DAY, 1, GETDATE()), 1)
INSERT INTO #t VALUES (1, GETDATE(), 1)
INSERT INTO #t VALUES (2, GETDATE(), 1)
INSERT INTO #t VALUES (1, DATEADD(DAY, 2, GETDATE()), 1)
INSERT INTO #t VALUES (1, DATEADD(DAY, 3, GETDATE()), 2)
SELECT * FROM #t
DELETE FROM #t WHERE unique_Id IN
(
SELECT unique_Id FROM
(
SELECT unique_Id
,Type_Id
,ROW_NUMBER() OVER (PARTITION BY worker_Id, type_Id ORDER BY date) AS rn
FROM #t
) Q
WHERE rn > 1
)
SELECT * FROM #t
you may use this query
delete from worker where unique_id in (
select max(unique_id) from worker group by worker_ID , type_ID having count(type_id)>1)
here i am assuming worker as your table name