Sort by value in json array in SQL Server - sql

I have table called foo like this :
create table foo
(
Id Int,
Description Nvarchar(Max)
)
I have json in array in Description like this:
[
{
"name":"a",
"date":"2021-03-01"
},
{
"name":"b",
"date":"2021-03-02"
}
]
I want sort foo table by max date in json list - something like this:
select *
from foo
order by json_value(Description, '$.date')
but this query does not work.
How can I fix this?

One possible approach is to parse the stored JSON with OPENJSON() and find the max date:
Table:
CREATE TABLE foo (
Id Int,
Description Nvarchar(Max)
)
INSERT INTO foo (Id, Description)
VALUES
(1, N'[{"name":"a","date":"2021-03-01"},{"name":"b","date":"2021-03-02"}]'),
(2, N'[{"name":"a","date":"2021-03-11"},{"name":"b","date":"2021-03-12"}]')
Statement:
SELECT Id, Description
FROM (
SELECT *
FROM foo f
OUTER APPLY (
SELECT MAX([date]) AS [Date]
FROM OPENJSON(f.Description) WITH ([date] nvarchar(10) '$.date')
) j
) t
ORDER BY TRY_CONVERT(date, [Date], 23)
Result:
Id Description
1 [{"name":"a","date":"2021-03-01"},{"name":"b","date":"2021-03-02"}]
2 [{"name":"a","date":"2021-03-11"},{"name":"b","date":"2021-03-12"}]

Related

Get max date based on another column in SQL

I have a table variable which consists of columns id, date and status as shown below
DECLARE #DateValues TABLE (
id int identity(1,1),
dates datetime,
status varchar (5)
)
And follows is the sample data
INSERT INTO #DateValues values ('5/22/2021','')
INSERT INTO #DateValues values ('5/21/2021','ABC')
INSERT INTO #DateValues values ('5/22/2021','ABC')
Also declared a variable as shown below.
DECLARE #MaxID INT
From this table I need to get the row which containing the maximum value of date( MAX(dates) ) where status is 'ABC', from the above sample values, I should get the 3rd row as the result and I need to assign the corresponding id value of the row id to a variable (#MaxID).
I tried following queries but getting multiple result set
SELECT id, MAX(dates), Footer
FROM #DateValues
WHERE STATUS = 'ABC'
GROUP BY id, STATUS
SELECT id, dates, status
FROM #DateValues
WHERE dates = (
SELECT MAX(dates)
FROM #DateValues
);
I need something like:
#MaxID = id
FROM #DateValues
WHERE dates = (
SELECT MAX(dates)
FROM #DateValues
WHERE STATUS='ABC'
);
Please help.
Is this what you want?
SELECT *
FROM (
SELECT *, RN = ROW_NUMBER() OVER (ORDER BY dates DESC)
FROM #DateValues
WHERE status = 'ABC'
) AS D
WHERE D.RN = 1

Function to multiple tables

I have this function, but I wanted to pass a table so as to use the same function to get the job done for multiple tables. For example, I want this function work for table1, and table2. But it is just for table1 currently. I was trying to use a dynamic sql in vain; it doesn't pass the parameter selected.
Can someone help? Give me guide on how to pass table as a parameter.
Sample data, table1
CREATE TABLE table1 (id int identity (1,1), name varchar(60))
INSERT INTO table1
VALUES ('a1, a2, a9, a8')
Sample data, table2
CREATE TABLE table2 (id int identity (1,1), name varchar(60))
INSERT INTO table2
VALUES ('a1, a2, a9, a8')
The function:
CREATE FUNCTION f_split
(#id INT)
RETURNS #ab
TABLE (name VARCHAR(20),
ab1 VARCHAR(5)
)
AS
BEGIN
DECLARE #temp TABLE (rn INT, name VARCHAR(5))
INSERT INTO #temp(rn, name)
SELECT ROW_NUMBER() OVER(ORDER BY LTRIM(RTRIM(Split.a.value('.', 'NVARCHAR(MAX)'))) ASC) rn, LTRIM(RTRIM(Split.a.value('.', 'NVARCHAR(MAX)'))) Result
FROM
(
SELECT CAST('<X>'+REPLACE([name], ',', '</X><X>')+'</X>' AS XML) AS String
FROM table1 where id = #id
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)
ORDER BY 1
INSERT INTO #ab
SELECT * FROM #temp
RETURN
END
This gives the result from table1.
SELECT * FROM F_SPLIT(1)
But I want the same function to work for table2 as well.
Any help is appreciated.
Use a partitioned view, which will allow you to specify the table name as a parameter in the where clause.
Start by creating a view that unions the two tables, plus an additional column to indicate which table the row comes from.
CREATE VIEW BothTables AS
SELECT 'Table1' TableName, * FROM Table1
UNION ALL
SELECT 'Table2' TableName, * FROM Table2
Then modify your function. When you pass the table name, use it to select a subset of rows from the view. So instead of
SELECT CAST('<X>'+REPLACE([name], ',', '</X><X>')+'</X>' AS XML) AS String
FROM table1
WHERE id = #id
Use
SELECT CAST('<X>'+REPLACE([name], ',', '</X><X>')+'</X>' AS XML) AS String
FROM BothTables
WHERE TableName = #TableName
AND id = #id

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

sorting SQL with substring on string

i have the data like this:
CODE_VD
N_10_19_xxx
N_0_3_xxx
N_121_131_xxx
N_100_120_xxx
N_80_90_xxx
N_20_29_xxx
as you can see i need to sort just the first number after N_,i don't know how can i get this number.
i have tried with susbsting(CODE_VD,2,3) but not exactly what i expected.
i want to get this:
CODE_VD
N_0_3_xxx
N_10_19_xxx
N_20_29_xxx
N_80_90_xxx
N_100_120_xxx
N_121_131_xxx
how can i do that ?
DECLARE #MyTable TABLE
(
CODE_VD VARCHAR(20)
)
INSERT INTO #MyTable
( CODE_VD )
VALUES
('N_10_19_xxx'),
('N_0_3_xxx'),
('N_121_131_xxx'),
('N_100_120_xxx'),
('N_80_90_xxx'),
('N_20_29_xxx');
SELECT * FROM
(
SELECT
*,
CONVERT(INT,
SUBSTRING(mt.CODE_VD,
3,
CHARINDEX('_', mt.CODE_VD, 3) - 3)) ConvCol
FROM #MyTable mt
) mt
ORDER BY mt.ConvCol
I converted to int to get the sort to work correctly, because 100 > 20
SELECT SUBSTRING(CODE_VD,3, CHARINDEX('_',CODE_VD, 3)-3)
declare #t Table (CODE_VD VARCHAR(MAX))
INSERT INTO #t (CODE_VD)VALUES ('N_10_19_xxx')
INSERT INTO #t (CODE_VD)VALUES ('N_0_3_xxx')
INSERT INTO #t (CODE_VD)VALUES ('N_121_131_xxx')
INSERT INTO #t (CODE_VD)VALUES ('N_100_120_xxx')
;WITH
sorted
AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY SUBSTRING(CODE_VD,3, CHARINDEX('_',CODE_VD, 3)-3) ORDER BY CODE_VD) AS sequence_id
FROM
#t
)
SELECT
CODE_VD
FROM
sorted
WHERE
sequence_id = 1

Is there a way to return more than 1 row in select without using existing tables

Simple question, just out of curiosity.
For example select 1,2,3 that will show table with one column and three rows.
Something like this: select values(1),(2),(3)
*with one select statement
An example for my comment in your post.
DECLARE #TABLE TABLE (ONE INT, TWO INT, THREE INT)
INSERT INTO #TABLE VALUES (1,2,3)
SELECT UP.COL, UP.VALUE
FROM #TABLE
UNPIVOT (VALUE FOR COL IN (ONE,TWO,THREE)) UP
Query:
DECLARE #t TABLE (i1 INT, i2 INT, i3 INT)
INSERT INTO #t VALUES (1, 2, 3)
SELECT t.*
FROM #t
CROSS APPLY (
VALUES(i1), (i2), (i3)
) t(value)
Output:
value
-----------
1
2
3
Additional info:
http://blog.devart.com/is-unpivot-the-best-way-for-converting-columns-into-rows.html
As it appears there is a simple code that I've been searching for:
select n from (values (1),(2),(3)) D(c);