MS Access SQL query for IP address range - sql

IP addresses are being stored as text values in an Access database in the format 192.168.0.1 - 192.168.0.254 in a junction table.
Junction table e.g Areas
Name IPAddress
Area1 192.168.0.1 - 192.168.0.254
Area2 192.168.1.1 - 192.168.1.254
I need to be able to search for records that between these ranges e.g.
SELECT * FROM devices WHERE ipaddress = 192.168.0.1 /Returns record Name1
or
SELECT * FROM tablename WHERE ipaddress BETWEEN 192.168.0.1 AND 192.168.0.25 /Returns record Name1,Name2,Name3,etc

A successful approach would be composed of three parts:
Parse the IPAddress column and split it up into two logical (text) columns: IPAddressLow and IPAddressHigh that capture the range of IPs for an area. Let's call this qryAreas:
select
[Name]
, ... as IPAddressLow
, ... as IPAddressHigh
from Areas
Implement a function (in VBA which you can then call from within Access SQL) to do comparisons on IP addresses. The comparator function could be something like:
' Returns:
' -1 if IP1 < IP2
' 0 if IP1 = IP2
' 1 if IP1 > IP2
Function CompareIPAddresses(ip1 As String, ip2 As String) As Integer
ip1_arr = Split(ip1, ".")
ip2_arr = Split(ip2, ".")
For i = 0 To 3
ip1_arr(i) = CLng(ip1_arr(i))
ip2_arr(i) = CLng(ip2_arr(i))
Next i
If ip1 = ip2 Then
retval = 0
ElseIf ip1_arr(0) < ip2_arr(0) Then
retval = -1
ElseIf ip1_arr(0) = ip2_arr(0) And ip1_arr(1) < ip2_arr(1) Then
retval = -1
ElseIf ip1_arr(0) = ip2_arr(0) And ip1_arr(1) = ip2_arr(1) And ip1_arr(2) < ip2_arr(2) Then
retval = -1
ElseIf ip1_arr(0) = ip2_arr(0) And ip1_arr(1) = ip2_arr(1) And ip1_arr(2) = ip2_arr(2) And ip1_arr(3) < ip2_arr(3) Then
retval = -1
Else
retval = 1
End If
CompareIPAddresses = retval
End Function
Use the above function in queries to figure out if an IP address is equal to a certain value or falls within a certain range. E.g., if you have an address 192.168.1.100 and want to find out which area it's in, you can do:
select [Name]
from qryAreas
where CompareIPAddresses(IPAddressLow, '192.168.1.100') in (-1, 0)
and CompareIPAddresses('192.168.1.100', IPAddressHigh) in (-1, 0)
The where clause here is the clunkier equivalent of the more elegant where 192.168.1.100 between IPAddressLow and IPAddressHigh syntax, because you don't have a native IP address data type and its corresponding operators--so you're rolling your own.

For searching on a range of IP addresses you might be able to use a little VBA function like this
Option Compare Database
Option Explicit
Public Function ZeroPaddedIP(IP As String) As String
Dim rtn As String, octets() As String, octet As Variant
rtn = ""
octets = Split(IP, ".")
For Each octet In octets
rtn = rtn & "." & Format(Val(octet), "000")
Next
ZeroPaddedIP = Mid(rtn, 2) ' trim leading "."
End Function
It pads the octets with leading zeros so
ZeroPaddedIP("192.168.0.1") --> "192.168.000.001"
and your query could do something like
SELECT * FROM tablename
WHERE ZeroPaddedIP(ipaddress) BETWEEN "192.168.000.001" AND "192.168.000.025"
That query will do a table scan because it cannot use any existing index on [ipaddress]. If performance is an issue, you might consider storing your IP addresses in padded form (either instead of, or in addition to the normal un-padded format).
Edit
For a test table named [NetworkData] ...
ID IP Description
-- ------------- -----------
1 192.168.0.1 router
2 192.168.0.2 test server
3 192.168.0.3 dev server
4 192.168.0.102 test client
5 192.168.0.103 dev client
... the VBA function shown above could be used in an Access query like this ...
SELECT
IP,
ZeroPaddedIP(IP) AS PaddedIP
FROM NetworkData
... to produce the following results ...
IP PaddedIP
------------- ---------------
192.168.0.1 192.168.000.001
192.168.0.2 192.168.000.002
192.168.0.3 192.168.000.003
192.168.0.102 192.168.000.102
192.168.0.103 192.168.000.103
... but only if the query is executed from within Access itself. The same results could be obtained from the following query, but this one will work if the query is run against the Access database from some other application (like Excel):
SELECT
IP,
Right('000' & Octet1, 3) & '.' & Right('000' & Octet2, 3) & '.' & Right('000' & Octet3, 3) & '.' & Right('000' & Octet4, 3) AS PaddedIP
FROM
(
SELECT
IP,
Octet1,
Octet2,
Left(TheRest2, InStr(TheRest2, '.') - 1) AS Octet3,
Mid(TheRest2, InStr(TheRest2, '.') + 1) AS Octet4
FROM
(
SELECT
IP,
Octet1,
Left(TheRest1, InStr(TheRest1, '.') - 1) AS Octet2,
Mid(TheRest1, InStr(TheRest1, '.') + 1) AS theRest2
FROM
(
SELECT
IP,
Left(IP, InStr(IP, '.') - 1) AS Octet1,
Mid(IP, InStr(IP, '.') + 1) AS theRest1
FROM NetworkData
) AS q1
) AS q2
) AS q3
So, if you were querying the data from Excel (or wherever) and you tried to use
SELECT * FROM NetworkData
WHERE IP Between '192.168.0.1' And '192.168.0.25'
you would get the following incorrect result
ID IP Description
-- ------------- -----------
1 192.168.0.1 router
2 192.168.0.2 test server
4 192.168.0.102 test client
5 192.168.0.103 dev client
whereas if you used
SELECT NetworkData.*
FROM
NetworkData
INNER JOIN
(
SELECT
IP,
Right('000' & Octet1, 3) & '.' & Right('000' & Octet2, 3) & '.' & Right('000' & Octet3, 3) & '.' & Right('000' & Octet4, 3) AS PaddedIP
FROM
(
SELECT
IP,
Octet1,
Octet2,
Left(TheRest2, InStr(TheRest2, '.') - 1) AS Octet3,
Mid(TheRest2, InStr(TheRest2, '.') + 1) AS Octet4
FROM
(
SELECT
IP,
Octet1,
Left(TheRest1, InStr(TheRest1, '.') - 1) AS Octet2,
Mid(TheRest1, InStr(TheRest1, '.') + 1) AS theRest2
FROM
(
SELECT
IP,
Left(IP, InStr(IP, '.') - 1) AS Octet1,
Mid(IP, InStr(IP, '.') + 1) AS theRest1
FROM NetworkData
) AS q1
) AS q2
) AS q3
) AS q4
ON q4.IP = NetworkData.IP
WHERE q4.PaddedIP Between '192.168.000.001' And '192.168.000.025'
you would receive the following correct result
ID IP Description
-- ----------- -----------
1 192.168.0.1 router
2 192.168.0.2 test server
3 192.168.0.3 dev server

Related

Extract value from double escaped JSON column in SQL

I have ports_list table with ports column below:
ports
intended_output
"[{\"id\":193,\"name\":\"PORT C\"}]"
PORT C
"[{\"id\":204,\"name\":\"PORT D\"}]"
PORT D
"[{\"id\":45,\"name\":\"PORT I\"},{\"id\":193,\"name\":\"PORT C\"},{\"id\":204,\"name\":\"PORT D\"},{\"id\":271,\"name\":\"PORT G\"}]"
PORT I, PORT C, PORT D, PORT G
I use this method and somehow managed to get result when there is only single port value in a row unfortunately it is unable to cater for multiple port values in a row:
select ports,
REPLACE( substring(ports, CHARINDEX('"name\":\', ports, 1)+10, LEN(ports)-10),
'\"}]"',
''
)
from ports_list
ports
output
"[{\"id\":193,\"name\":\"PORT C\"}]"
PORT C
"[{\"id\":204,\"name\":\"PORT D\"}]"
PORT D
"[{\"id\":45,\"name\":\"PORT I\"},{\"id\":193,\"name\":\"PORT C\"},{\"id\":204,\"name\":\"PORT D\"},{\"id\":271,\"name\":\"PORT G\"}]"
PORT I"},{"id":193,"name":"PORT C"},{"id":204,"name":"PORT D"},{"id":271,"name":"PORT G"}]"
Can someone help me to get the intended output as per the first table?
Your ports column is a double-escaped JSON, in other words, it contains a JSON-escaped string that represents a JSON object.
So we just need to get it back into regular JSON to query it. Let's stuff it into a [] JSON array, then pass it to JSON_VALUE:
select ports,
STRING_AGG(j.name, ', ')
from ports_list
outer apply OPENJSON(JSON_VALUE('[' + ports + ']', '$[0]'))
with (name nvarchar(100) '$.name') as j
group by ports;
You created a soluntion to resolve only when exists one port.
The function CharIndex is returning the first ocorrency of '"name":' always and the function SUBSTRING always return the string minus the ten last characters.
A form to resolve the problem is creating the user function to return all ocorrency of port.
try create the function below
CREATE FUNCTION uf_findPorts
(
#string nvarchar(max)
)
RETURNS nvarchar(MAX)
AS
BEGIN
DECLARE
#length INT ,
#aux INT ,
#return NVARCHAR(MAX)
SET #length = LEN(#string)
SET #aux = 1
SET #return = ''
WHILE(CHARINDEX('"name\":\', #string,#aux) != 0)
BEGIN
SET #return = #return + ' ' + REPLACE( substring(#string,
CHARINDEX('"name\":\', #string,#aux)+10,
6),
'\"}]"',
''
)
SET #string = SUBSTRING(#string,CHARINDEX('"name\":\', #string,#aux) + 6,LEN(#string) )
END
RETURN #return
END
GO
To Test:
select ports,dbo.uf_findPorts(ports)
from ports_list
SELECT dbo.uf_findPorts('[{\"id\":45,\"name\":\"PORT I\"},{\"id\":193,\"name\":\"PORT C\"},{\"id\":204,\"name\":\"PORT D\"},{\"id\":271,\"name\":\"PORT G\"}]')
SELECT dbo.uf_findPorts('"[{\"id\":193,\"name\":\"PORT C\"}]"')
OBS: If exists any error of write you can corret me
Thanks to those who tried to help.
Posting my solution here if anyone stumbled into similar problem in the future:
select ports = replace(replace(replace(dbo.fnRemovePatternFromString(dbo.fnRemovePatternFromString(dbo.fnRemovePatternFromString(dbo.fnRemovePatternFromString(ports,'%[\"{}]%',1),'%name:%',5),'%id:%',3),'%[0-9]%',1),'[,',''),',,',','),']','')
from ports_list

How AND/OR operators works? [duplicate]

This question already has answers here:
SQL Logic Operator Precedence: And and Or
(5 answers)
Closed 4 years ago.
I'm Reading book about sql and I see some statements using or/and and I don't understand them:
this is the main statement:
SELECT ∗
FROM administrators
WHERE username = ’’ AND password = ’’;
if some one try to do an sql bypass , he will do this:
SELECT ∗
FROM administrators
WHERE username = ” OR ‘1’=‘1’ AND password = ”;
or this
SELECT ∗
FROM administrators
WHERE (username = ’’) OR (‘1’=‘1’ AND password = ’’);
how these 2 statements get the same results, I don't understand how AND/OR works in theses statements ..
and the last question how these statements return all value in database (bypass the auth):
select *
from users
where (username = '') or (1=1) or (1=1 AND password = '') ;
OR
SELECT ∗
FROM administrators
WHERE username = ’’ AND
password = ’’ OR
1’=‘1’;
In simple explanations:
SELECT ∗ FROM administrators WHERE username = ” OR ‘1’=‘1’ AND password = ”;
Here if the username exists and password is wrong, it will return all columns for that username else returns nothing.
SELECT ∗ FROM administrators WHERE (username = ’’) OR (‘1’=‘1’ AND password = ’’);
This returns the same thing as the above, the brackets don't matter.
SELECT ∗ FROM administrators WHERE username = ’’AND password = ’’ OR ‘1’=‘1’ ;
This makes a difference, even if both username and password are wrong, it will return the full * columns. [best option for a SQL injection for full table data]
select * from users where (username = '') or (1=1) or (1=1 AND password = '') ;
same results as above
Its easy to think of it this way:
any AND/OR condition introduced after where is paired for the first constraint, any other introduced after that is a constraint of its own.
WHERE condition1 OR/AND condition1-pair AND separate condition
You can consider AND as multiplication, OR as addition, true statement as 1 and false statement as 0. For example statement
SELECT .... WHERE y = 0 AND x < 1 OR 1 = 1
will always be true, because
1*1 + 1 = 1
0*0 + 1 = 1
1*0 + 1 = 1
(0 and 1 are Boolean, not decimal)

Select part of word from query?

In Access, I have a field like this:
From the start until end at 01/05/2013, the XXXXXXX device was used.
I'm looking for a query that can extract the device name (in this case XXXXXXX). I was able to use mid() to get it somewhat working, but the sentence could be longer or shorter. What are some other ways to do this? Is there a find() I can use to find the location of "device"?
My SQL looks like:
SELECT Mid(Note, 68, 30)
FROM My_Log
WHERE Note LIKE "*, the*"
;
If the device name is the word before "device", and you want to find that word using only functions supported directly by the Access db engine, Mid(), InStr(), and InstrRev() can get the job done ... but it won't be pretty.
Here is an Immediate window session ...
Note = "From the start until end at 01/05/2013, the XXXXXXX device was used."
? Mid(Note, _
InstrRev(Note, " ", InStr(1, Note, " device") -1) +1, _
InStr(1, Note, " device") - InstrRev(Note, " ", InStr(1, Note, " device") -1) -1)
XXXXXXX
So you would then need to use that complex Mid() expression in a query.
However, if you can use a user-defined function in your query, the logic could be easier to manage.
Public Function FindDevice(ByVal pInput As String) As Variant
Dim astrWords() As String
Dim i As Long
Dim lngUBound As Long
Dim varReturn As Variant
varReturn = Null
astrWords = Split(pInput, " ")
lngUBound = UBound(astrWords)
For i = 0 To lngUBound
If astrWords(i) = "device" Then
varReturn = astrWords(i - 1)
Exit For
End If
Next i
FindDevice = varReturn
End Function
Then the query could be ...
SELECT FindDevice(Note) AS device_name
FROM My_Log
WHERE Note LIKE "*, the*"
If you know the name of the device, you can use Instr:
... WHERE Instr([Note], "Mydevice")>0
You can also use a table that lists devices:
SELECT Note FROM MyTable, ListTable
WHERE Note Like "*" & ListTable.Devices & "*"
Re comment:
SELECT Note
FROM MyTable
WHERE [Note] Like "*" & [enter device] & "*"
If you need to know where in notes you will find device
SELECT Note, InStr([Note],[enter device]) ...
In addition to my comments this is Oracle query that may help you. Replace dual with with your table name and query should probably work. As I mentioned all functions I'm using in this query are ANSI SQL standard. The syntax you can fix yourself. You may run all queries separately to get start, end positions and lenght of your machine name, etc...:
SELECT SUBSTR(str, start_pos, end_pos) machine_name
FROM
(
SELECT str, INSTR(str,'XXXXXXX') start_pos, Length('XXXXXXX') end_pos
FROM
(
SELECT 'From the start until end at 01/05/2013, the XXXXXXX device was used.' str FROM dual
)
)
/
SQL>
MACHINE
-------
XXXXXXX
More generic approach with the same result - eliminating Length:
SELECT SUBSTR(str_starts, 1, end_pos) machine_name FROM -- Final string
(
SELECT str_starts, INSTR(str_starts, ' ')-1 end_pos FROM -- end pos = last X pos in string starts - in 'XXXXXXX'
(
SELECT SUBSTR(str, start_pos) str_starts FROM -- strig starts from first X
(
SELECT str, INSTR(str,'XXXXXXX') start_pos FROM -- start_pos
(
SELECT 'From the start until end at 01/05/2013, the XXXXXXX device was used.' str FROM dual
))))
/

How to extract data from variable string and use in a select query

I have a question regarding how to extract data from string (variable length) and include it in the select queries.
For example, I have value in Portfolio_full_name as TS.PDO.CTS
(Portfolio_full_name = TS.PDO.CTS)
I would like to retrieve each word before the . and put it into another fields.
Portfolio_name = TS
Portfolio_category = PDO
Portfolio_subcategory = CTS
I am looking for to include this in the select statement before where condition (create CASE statement maybe?) Could you please let me know how could I do this?
In SQL Server (assuming that the format is fixed to NAME.CATEGORY.SUBCATEGORY and you've got the Portfolio_full_name column in some table, called atable here, and are updating the columns Portfolio_name, Portfolio_category and Portfolio_subcategory in the same table):
UPDATE atable
SET
Portfolio_name = SUBSTRING(s.Portfolio_full_name, 1, DotPos1 - 1),
Portfolio_category = SUBSTRING(s.Portfolio_full_name, DotPos1 + 1, DotPos2 - DotPos1 - 1),
Portfolio_subcategory = SUBSTRING(s.Portfolio_full_name, DotPos2 + 1, FullLen - DotPos2)
FROM (
SELECT
Portfolio_full_name,
DotPos1 = CHARINDEX('.', Portfolio_full_name),
DotPos2 = CHARINDEX('.', Portfolio_full_name, CHARINDEX('.', Portfolio_full_name) + 1),
FullLen = LEN(Portfolio_full_name)
FROM (
SELECT Portfolio_full_name FROM atable
) s
) s
WHERE atable.Portfolio_full_name = s.Portfolio_full_name

How to substract two Sums in Maple

I've got two sequences with variable boundaries like
> a:=Sum(x(i),i=n..m);
> b:=Sum(x(i),i=n-1..m+1);
n and m are arbitrary natural numbers and obviously m>n.
I want to substract a from b and see how Maple simplifies the expression to
> b-a;
x(n-1)+x(m+1);
Is it possible in Maple or in another CAS?
You might do it by using a temporary object,and then acting in two stages.
a:=Sum(x(i),i=n..m):
b:=Sum(x(i),i=n-1..m+1):
temp := Sum(x(i),i=op(1,rhs(op(2,a)))..op(2,rhs(op(2,b))));
m + 1
-----
\
)
/ x(i)
-----
i = n
value( combine(b-temp) + combine(temp-a) );
x(n - 1) + x(m + 1)
Or you might put that into a procedure.
combminus:=proc(s::specfunc(anything,Sum),t::specfunc(anything,Sum))
local temp;
if op(1,s) = op(1,t) then
temp:=Sum(op(1,s),i=op(1,rhs(op(2,s)))..op(2,rhs(op(2,t))));
value(combine(s-temp)+combine(temp-t));
else
s-t;
end if;
end proc:
combminus(b, a);
x(n - 1) + x(m + 1)