Match any patterns in a table? - sql-server

I know we can use LIKE for pattern matching, however, here is what want to do.
I have a table, which has a column, 'Pattern', the values are like:
host1%
%host2
....
I have another table, which has a column, 'Host'. The question is: how can I check whether the values in 'Host' table do not match any patterns in 'Pattern'?
If it is too complex, then a simplified question is: How can I check whether the values in 'Host' do not StartWith any strings in 'Pattern'?
We can use loop, but is there a better way? ideally, it should work for ql server 2008, but latest version will do.
thanks

Use where not exists followed by a subquery which checks each pattern against the current row of the table containing your data. i.e.
where not exists
(
select top 1 1
from #patterns p
where d.datum like p.pattern
)
Full Code for Working Example: SQL Fiddle
declare #patterns table
(
pattern nvarchar(16) not null
)
declare #data table
(
datum nvarchar(16) not null
)
insert #patterns
values ('host1%')
,('%host2')
insert #data
values ('host1234')
, ('234host1')
, ('host2345')
, ('345host2')
select *
from #data d
where not exists
(
select top 1 1
from #patterns p
where d.datum like p.pattern
)

select t1.host
from table_1 t1
left join table_2 t2 on t1.host like t2.pattern
where t2.pattern is null

Related

SQL Server 2016 - Array into separate columns

I'm tasked with importing data into SQL thats pretty much JSON but not quite . I've used OPENROWSET/OPENJSON to import into a staging table and the data looks like this
What I need to achieve is migrate that to a single table with the following structure
I'm having no success , I even trying updating the data in the staging table to look like this and import but no joy.
My current attempt:
SELECT A.[DATE], A.[VALUE]
FROM OPENJSON(#JSON) AS I
CROSS APPLY (
SELECT *
FROM OPENJSON (#JSON) WITH (
[DATE] NVARCHAR(MAX) '$.DATE',
[VALUE] NVARCHAR(MAX) '$.VALUE'
)
) A OUTPUT
Any recommendations ?
Just use this way:
CREATE TABLE #tmp (
instance NVARCHAR(50),
json NVARCHAR(1000)
)
INSERT #tmp
VALUES
( N'server1.com',
N'[{"date":10000, "value":"6"},{"date":20000, "value":"8"}]'
)
SELECT
t.instance, Date,Value
FROM #tmp t
OUTER APPLY OPENJSON(t.json)
WITH (
Date varchar(200) '$.date' ,
Value VARCHAR(100) '$.value'
)
For your first set of data, you have a doubly-nested JSON array, so you need to use OPENJSON to break open the outer one first:
SELECT
instance
JSON_VALUE(j1.innerArray, '$[0]') AS date,
JSON_VALUE(j1.innerArray, '$[1]') AS value
FROM table t
CROSS APPLY OPENJSON(t.json) WITH (
innerArray nvarchar(max) '$' AS JSON
) j1
For the second version, just change the JSON_VALUE parameters:
JSON_VALUE(j1.innerArray, '$.date') AS date,
JSON_VALUE(j1.innerArray, '$.value') AS value
Original answer:
The reason for the unexpected result is the fact, that the you have nested JSON arrays, but the WITH clause is not correct. You need to use the appropriate WITH clause, like the statement below:
Table:
SELECT *
INTO Data
FROM (VALUES
('server1.com', '[[1613347200, "7"], [1613347205, "8"], [1613347202, "9"]]'),
('server2.com', '[[1613317200, "3"], [1613347215, "2"], [1613347212, "1"]]')
) v (instance, array)
Statement:
SELECT d.instance, j.[date], j.[value]
FROM Data d
OUTER APPLY OPENJSON(d.array) WITH (
[date] numeric(10, 0) '$[0]',
[value] varchar(1) '$[1]'
) j
Result:
instance date value
-----------------------------
server1.com 1613347200 7
server1.com 1613347205 8
server1.com 1613347202 9
server2.com 1613317200 3
server2.com 1613347215 2
server2.com 1613347212 1
Update:
Your second attempt is almost correct. The reason for the NULL values is the fact, that the path part of the columns definitions in the WITH clause is case sensitive:
SELECT d.instance, j.[date], j.[value]
FROM (VALUES
('server1.com', '[{"date":1613347200, "value":"7"}, {"date":1613347200, "value":"8"}]')
) d (instance, array)
OUTER APPLY OPENJSON(d.array) WITH (
[date] numeric(10, 0) '$.date',
[value] varchar(1) '$.value'
) j

Is it possible to perform a regex pattern match in a SQL Server varchar(max) column and return the match?

I have a log table that has some records that have this type of pattern:
.... "RefundId":"re_1ABasdf234234343434", "..."....
I want to extract and return the value of the RefundId in a column in a select statement, is this possible?
If there is only one Refund_ID for each row then you can use something like this:
--Create table
create table T1 (
T1_id int identity(1,1) primary key clustered,
Log_Data varchar(max) null
)
--Insert test data
insert T1(Log_Data)
values('.... "RefundId":"re_1ABasdf234234343434", "..."....'),
(' "RefundId":"JHHJJHJHJHJJHJH", "..."....'),
(''),
(null)
--Get some results
select *, left(substring(Log_Data, patindex('%"RefundId":"%', Log_Data)+12, 20000000), patindex('%"%', substring(Log_Data, patindex('%"RefundId":"%', Log_Data)+12, 20000000)) + case when patindex('%"%', substring(Log_Data, patindex('%"RefundId":"%', Log_Data)+12, 20000000)) > 0 then -1 else 0 end ) Refund_ID
from T1
If there are multiple Refund_IDs for each value then you will have to find a different method.
You can use the keyword LIKE
SELECT RefundId
FROM MyTable
WHERE RefundId LIKE 'some pattern'

SQL Server : how to update and replace with wildcards

How exactly do I replace something by using wildcard characters?
I had tried with this but it doesn't work
UPDATE [dbo].[test]
SET [Fælge] = REPLACE([Fælge],'%ET%%%','')
I want all the ET** to be blank
My data looks like this and it goes from 4-12x10-24 ET0-99 plus a half like(4.5x13 ET35.5)
6x15 ET0|6.5x16 ET55|6x16 ET50|7x17 ET60|7x17 ET65
my data grouped and counted
i want both ET and the numbers to be blank so the data just looks like
6x15 |6.5x16 |6x16 |7x17 |7x17
I will assume that the sample data provided is indicating multiple rows. You can do this quite easily using LEFT and CHARINDEX.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
SomeValue varchar(50)
)
insert #Something(SomeValue) values
('6x15 ET0'),
('6.5x16 ET55'),
('6x16 ET50'),
('7x17 ET60'),
('7x17 ET65')
update #Something
set SomeValue = rtrim(LEFT(SomeValue, CHARINDEX('ET', SomeValue) - 1))
select *
from #Something

SQL Server - Test to see if Table A's values are in Table B

I have created two table variables (each have one column):
Declare #CoursePrereq TABLE (Prereq char(6))
Declare #TakenCourses TABLE (Taken char(6))
Basically, I want to test to see if ALL of the prerequisite courses are stored in the taken column of the #TakenCourses table. If they are, that means the prerequisites have been taken and I'd like a 1 (for true) returned. Obviously, i'd like a 0 if ALL values from #CoursePrereq are not in #TakenCourses.
Is there a way to test to see if these Prereq Column values are ALL in the Taken Column? I've tried a few things (while loop with top 1, if statement, exists) and i'm stuck. Can I get some assistance?
Thanks in advance!
You can use NOT EXISTS to find the missing Prereq values in Taken column. And then use CASE expression to output 1 and 0 based on the count of the result set.
Query
SELECT CASE WHEN COUNT(*) > 0 THEN 0 ELSE 1 END AS [Status]
FROM #CoursePrereq t
WHERE NOT EXISTS (
SELECT 1 FROM #TakenCourses
WHERE t.Prereq = Taken
);
SQL Fiddle Demo
Try with exists:
Declare #CoursePrereq TABLE (Prereq char(6))
Declare #TakenCourses TABLE (Taken char(6))
insert into #CoursePrereq values ('1'),('2'),('3')
insert into #TakenCourses values ('1'),('2'),('3'),('4')
select case when count(*) > 1 then 1 else 0 end as 'MATCHING' from #CoursePrereq
where exists
(select * from #TakenCourses where Prereq = Taken)
EXAMPLE

How to select max value from n number linked tuples

I'm trying to select the max id from any n number linked tuples in a sql server db - we are writing an upgrade script for some data sets within an app, and need to know what the highest upgrade available is based on what the data's version is currently. For example, using the following simplified table 'versions':
oldVersionId newVersionId
1 2
2 3
3 4
10 11
We know we are version 1, and want to get the highest version out there that we can upgrade to; which would return 4 in this case, and not 11. We can have 0-n number of upgradable versions available at any given time. I'm not an sql wiz, and could only think to query using a variable number of chained selects:
select newVersion from versions where oldVersionId = (select newVersion from versions where oldVersionId = 1)
But it not an n numbered search, and won't return correctly if the number of elements is greater or less then the given. Is sql capable of performing such a query, and what elements / keywords should I be looking at to write one?
Solution:
You learn something new every day - I needed a hybrid of the two answers. Turns out sql can query a dataset using the tree-like child/parent linking that I'm way more comfortable with in OO languages.
In sql server you can set up a recursive tree walking call using a aliased table. You need an anchor and then the recursive bit. The first call is the anchor, I can use any value in the table, or a list of values, etc. The second select call just says to use the rest of the table to scan against.
Here is the syntax:
--Create the new alias (s)
;with s (oldVersionId, newVersionId) as
(
--set up the anchor node,
select oldVersionId, newVersionId from #t
where oldVersionId = 1
-- join it to the rest of the table, denoting that we only want nodes
-- where the old version is represented as a new version later
union all
select t.oldVersionId, t.newVersionId from #t as t
inner join s on t.oldVersionId = s.newVersionId
)
--Return the max value from the nodes I collected
select max(s.newVersionId) from s
Here is the solution with CTE - loop thru data while we have next match on oldVersionId = newVersionId:
declare #t table (
oldVersionId int,
newVersionId int )
insert into #t values (1,2)
insert into #t values (2,3)
insert into #t values (3,4)
insert into #t values (10,11)
insert into #t values (11,12)
insert into #t values (14,15)
declare #startVer int
set #startVer = 1
;with s (oldVersionId, newVersionId) as
(
select top 1 oldVersionId, newVersionId from #t
where oldVersionId = #startVer
union all
select t.oldVersionId, t.newVersionId from #t as t
inner join s on t.oldVersionId = s.newVersionId
)
select max(s.newVersionId)
from s
option (maxrecursion 0)
And here is solution without CTE - search for the last record which has newVersionId equals to 1 (1st version) plus the sum of imcremental updates to this version:
select max(t1.newVersionId)
from #t t1
where t1.oldVersionId >= #startVer
and t1.newVersionId = #startVer + (
select sum(newVersionId - oldVersionId)
from #t
where
oldVersionId >= #startVer and
oldVersionId < t1.newVersionId)
This problem can be solved by writing recursive queries to traverse recursive hierarchies in a table.
http://www.mssqltips.com/sqlservertip/1520/recursive-queries-using-common-table-expressions-cte-in-sql-server/
http://msdn.microsoft.com/en-us/library/ms186243.aspx
You effectively need to identify the final node in a linked list--seems to me your best bet would be to use the recursive features of CTEs to get to your 'max' version, but I'm not familiar enough with CTEs to get it working.
The following gets to the right answer, but only because I know how many links this particular dummy table will require beforehand; thus, not ideal.
CREATE TABLE #temp (
oldversionID SMALLINT,
newversionID SMALLINT )
INSERT INTO #temp
VALUES (1,2)
INSERT INTO #temp
VALUES (2,3)
INSERT INTO #temp
VALUES (3,4)
INSERT INTO #temp
VALUES (10,11);
select t1.oldversionID, t3.newversionID from #temp t1
inner join #temp t2
on t1.newversionId = t2.oldversionID
inner join #temp t3
on t2.newversionId = t3.oldversionID
Your problem is pretty much same as How to get the parent given a child in SQL SERVER 2005
I think same CTE will work for you.

Resources