Parse JSON string in SQL - sql

I have a column of JSON strings in my SQL table. I want to extract the 'page' value, any idea how?
{"action.type":"click","page":1424}

Hive actually has a command just for that get_json_object

Here is a pure SQL version.
DECLARE #json varchar(64);
DECLARE #index int;
SELECT #json = '{"action.type":"click","page":1424}';
DECLARE #length int = LEN(#json);
DECLARE #pageIndex int = CHARINDEX('page":', #json);
DECLARE #difference int = #length - (#pageIndex + 6); -- 6 is for page":
SELECT #index = CHARINDEX('page', #json);
SELECT SUBSTRING(#json, #index + 6, #difference);
This will give you a result of 1424
It is really long-winded, but it shows step-by-step how it get's that value. You can easily refactor that into a stored procedure.

Related

Replace last matched substring of string

I want to replace a matched last substring of string
Example
DECLARE #string NVARCHAR(MAX)= 'Greeting<br><br>As many offices move to remote work. <br><br>Have a good day<br><br>'
SELECT REPLACE(#string,'<br><br>','')
Result need
#string = 'Greeting<br><br>As many offices move to remote work. <br><br>Have a good day'
Need to replace the last two with empty
If the performance is not an issue, you can try reversing the string and then cut the first section and reverse it back
DECLARE #string NVARCHAR(MAX)= 'Greeting<br><br>As many offices move to remote work. <br><br>Have a good day<br><br>'
declare #pat nvarchar(max) = (select REVERSE('<br><br>'))
declare #rsv nvarchar(max) = (select REVERSE(#string))
declare #idx int = (select CHARINDEX(#pat, #rsv, 0))
declare #result nvarchar(max) = (select left(#rsv, #idx - 1) + right(#rsv, len(#rsv) - #idx - LEN(#pat) + 1))
set #result = reverse(#result)
print #result
Result is:
Greeting<br><br>As many offices move to remote work. <br><br>Have a good day
You can shorten the code. I just made it verbose so the logic will be clear.
If it's always on the end another simpler method would be to add a special character to the string then replace including that.
select #string=Replace(#string+'~','<br><br>~','')

Find all ERDs containing table with specified name

Is it possible that I can query for names of all ERDs (Entity Relationship Diagram) which contain table with name
Like '%mytable%'
Something like this:
select *
from <ERD objects>
where tableName like '%%'
Actually, I have a large Database with lots of ERDs. So, to understand the scope of a table I want to browse ERDs of that specific table.
As I understand you need a list of tables contained in a database diagrams. There you can help this article. I will add part of it here:
The diagram itself is stored in a binary field. And you can not convert it into readable form without difficulties. For deserializing this field (called definition) we need 2 functions:
CREATE FUNCTION [dbo].[Tool_VarbinaryToVarchar_Text]
(
#VarbinaryValue VARBINARY(max),
#bitASCIIOnly BIT = 0
)
RETURNS VARCHAR(max) AS
BEGIN
DECLARE #NumberOfBytes INT
SET #NumberOfBytes = DATALENGTH(#VarbinaryValue)
-- PART ONE --
IF (#NumberOfBytes > 4)
BEGIN
DECLARE #FirstHalfNumberOfBytes INT
DECLARE #SecondHalfNumberOfBytes INT
SET #FirstHalfNumberOfBytes = #NumberOfBytes/2
SET #SecondHalfNumberOfBytes = #NumberOfBytes - #FirstHalfNumberOfBytes
-- Call this function recursively with the two parts of the input split in half
RETURN dbo.Tool_VarbinaryToVarchar_Text(CAST(SUBSTRING(#VarbinaryValue, 1 , #FirstHalfNumberOfBytes) AS VARBINARY(max)),#bitASCIIOnly)
+ dbo.Tool_VarbinaryToVarchar_Text(CAST(SUBSTRING(#VarbinaryValue, #FirstHalfNumberOfBytes+1 , #SecondHalfNumberOfBytes) AS VARBINARY(max)),#bitASCIIOnly)
END
IF (#NumberOfBytes = 0)
BEGIN
RETURN '' -- No bytes found, therefore no 'hex string' is returned
END
-- PART TWO --
DECLARE #HighByte INT
-- #NumberOfBytes <= 4 (four or less characters/8 hex digits were input)
-- eg. 88887777 66665555 44443333 22221111
-- We'll process ONLY the right-most (least-significant) Byte, which consists
-- of eight bits
-- 2. Carve off the rightmost eight bits/single hex digit (ie 22221111)
-- Divide by 16 does a shift-left (now processing 2222)
SET #HighByte = CAST(#VarbinaryValue AS INT) & 255
IF #bitASCIIOnly = 1 AND (#HighByte < 32 OR #HighByte > 126) SET #HighByte=13;
-- 3. Trim the byte (two hex values) from the right (least significant) input Binary
-- in preparation for further parsing
SET #VarbinaryValue = SUBSTRING(#VarbinaryValue, 1, (#NumberOfBytes-1))
-- 4. Recursively call this method on the remaining Binary data, concatenating the text
-- 'value' we just decoded as their ASCII character representation
-- ie. we pass 88887777 66665555 44443333 back to this function, adding X to the result string
RETURN dbo.Tool_VarbinaryToVarchar_Text(#VarbinaryValue,#bitASCIIOnly) +
CHAR(#HighByte)
END
And:
CREATE FUNCTION [dbo].[fnTool_ScriptDiagram2005_Text]()
RETURNS
#tblOut TABLE
(
-- Add the column definitions for the TABLE variable here
diagramname NVARCHAR(128),
diagram_id INT PRIMARY KEY,
diagram_text VARCHAR(MAX),
diagram_ASCII VARCHAR(MAX)
)
AS
BEGIN
DECLARE #name NVARCHAR(128);
DECLARE #diagram_id INT;
DECLARE #index INT;
DECLARE #size INT;
DECLARE #chunk INT;
DECLARE #line VARCHAR(MAX);
DECLARE #lineASC VARCHAR(MAX);
DECLARE #CurrentPos INT;
SELECT #CurrentPos = MIN(diagram_id) FROM dbo.sysdiagrams;
WHILE (#CurrentPos IS NOT NULL)
BEGIN
-- Set start index, and chunk 'constant' value
SET #index = 1; --
SET #chunk = 32; -- values that work: 2, 6
-- values that fail: 15,16, 64
SELECT #diagram_id = diagram_id,
#size = DATALENGTH(definition),
#name = name
FROM dbo.sysdiagrams
WHERE diagram_id = #CurrentPos;
-- Now with the diagram_id, do all the work
SET #line = '';
SET #lineASC = '';
WHILE #index < #size
BEGIN
-- Output as many UPDATE statements as required to append all the diagram binary
-- data, represented as hexadecimal strings
SELECT #line = #line + dbo.Tool_VarbinaryToVarchar_Text(SUBSTRING (definition, #index, #chunk),0),
#lineASC = #lineASC + dbo.Tool_VarbinaryToVarchar_Text(SUBSTRING (definition, #index, #chunk),1)
FROM dbo.sysdiagrams
WHERE diagram_id = #CurrentPos;
SET #index = #index + #chunk;
END
INSERT INTO #tblOut (diagramname, diagram_id, diagram_text, diagram_ASCII)
VALUES (#name, #diagram_id, #line, REPLACE(#lineASC,CHAR(13),''));
SELECT #CurrentPos = MIN(diagram_id)
FROM dbo.sysdiagrams
WHERE diagram_id > #CurrentPos;
END
RETURN;
END
After that you can run:
SELECT *
FROM [dbo].[fnTool_ScriptDiagram2005_Text] ()
WHERE diagram_ASCII LIKE '%TableToFind%'
For example I have created diagram TestDiagram with 2 tables named whatever and IE_Stat. In return query:
Root EntrypfoBCompObj_ !"#$%&'()*+,-.123456789:(}5n]4o[\0V?[?i???V?[?i??T,,,4") -bH''Uu94941#xV4XdboIE_StatMicrosoft DDS Form 2.0Embedded Object9q&sch_labels_visibled(ActiveTableViewMode1 TableViewMode:0:4,0,28DdsStreamSchema UDV Default&/DSREF-SCHEMA-CONTENTS,0Schema UDV Default Post V66;4,0,2310,1,1890,5,1260 TableViewMode:12,0,284,0,2805 TableViewMode:22,0,284,0,2310 TableViewMode:32,0,284,0,2310 TableViewMode:4>4,0,284,0,2310,12,2730,11,1680(ActiveTableViewMode1 TableViewMode:0:4,0,284,0,2310,1,1890,5,1260 TableViewMode:12,0,284,0,2805 TableViewMode:22,0,284,0,2310 TableViewMode:32,0,284,0,2310 TableViewMode:4>4,0,284,0,2310,12,2730,11,1680NaQW9 LHEData Source=********;Initial Catalog=test;Integrated Security=True;MultipleActiveResultSets=False;TrustServerCertificate=True;Packet Size=4096;Application Name="Microsoft SQL Server Management Studio"TestDiagram&whateverdbo$IE_StatdbokE7d2pN{1634CDD7-0888-42E3-9FA2-B6D32563B91D}bR
you can see both table names.

Creating multiple UDFs in one batch - SQL Server

I'm asking this question for SQL Server 2008 R2
I'd like to know if there is a way to create multiple functions in a single batch statement.
I've made the following code as an example; suppose I want to take a character string and rearrange its letters in alphabetical order. So, 'Hello' would become 'eHllo'
CREATE FUNCTION char_split (#string varchar(max))
RETURNS #characters TABLE
(
chars varchar(2)
)
AS
BEGIN
DECLARE #length int,
#K int
SET #length = len(#string)
SET #K = 1
WHILE #K < #length+1
BEGIN
INSERT INTO #characters
SELECT SUBSTRING(#string,#K,1)
SET #K = #K+1
END
RETURN
END
CREATE FUNCTION rearrange (#string varchar(max))
RETURNS varchar(max)
AS
BEGIN
DECLARE #SplitData TABLE (
chars varchar(2)
)
INSERT INTO #SplitData SELECT * FROM char_split(#string)
DECLARE #Output varchar(max)
SELECT #Output = coalesce(#Output,' ') + cast(chars as varchar(10))
from #SplitData
order by chars asc
RETURN #Output
END
declare #string varchar(max)
set #string = 'Hello'
select dbo.rearrange(#string)
When I try running this code, I get this error:
'CREATE FUNCTION' must be the first statement in a query batch.
I tried enclosing each function in a BEGIN END block, but no luck. Any advice?
Just use a GO statement between the definition of the UDFs
Not doable. SImple like that.
YOu can make it is one statement using a GO between them.
But as the GO is a batch delimiter.... this means you send multiple batches, which is explicitly NOT Wanted in your question.
So, no - it is not possible to do that in one batch as the error clearly indicates.

How to check the integrity of a file in SQL Server 2005 inserted into a BLOB - varbinary(max)?

I'm trying to do a check for a file uploaded to a varbinary column in SQL Server 2005.
I uploaded the file and using
SELECT DATALENGTH(thefile) FROM table
I get the same number of bytes that the file has.
CHECKSUM is not the better way and HASHBYTES only takes the first 8000 bytes, and my files are so more great than that.
I can only use T-SQL.
Any tip will be helpful.
Thanks a lot :)
You can use hash over hash, it has the same strength as single-pass hash:
CREATE FUNCTION dbo.GetMyLongHash(#data VARBINARY(MAX))
RETURNS VARBINARY(MAX)
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
DECLARE #res VARBINARY(MAX) = 0x
DECLARE #position INT = 1, #len INT = DATALENGTH(#data)
WHILE 1 = 1
BEGIN
SET #res = #res + HASHBYTES('MD5', SUBSTRING(#data, #position, 8000))
SET #position = #position+8000
IF #Position > #len
BREAK
END
RETURN #res
END
GO
declare #theHash varbinary(max)
select #theHash = dbo.GetMyLongHash(thefile) from table
WHILE DATALENGTH(#thehash) > 16 SET #TheHash = dbo.GetMyLongHash(#theHash)
Of course you can modify the function for returning already final-pass hash

T-SQL: Concept similar to C# params

Does T-SQL allow a variable number of arguments to a stored procedure like params in C#?
EDIT: I'm using SQL Server 2005. That 2008 answer makes me wish we were using it...
In SQL 2008 there's Table-Valued Parameters (TVPs)
Your stored proc can accept lists of parameters..
Finally we're able to do a IN clause without relying on XML!
Mike
No, not for things like UDFs or stored procedures. That's what tables are for. Put the values in a table somewhere (with a common key) and pass the correct key to your procedure.
Typically
CREATE PROCEDURE dbo.sptest
( #xml TEXT )
AS
BEGIN
DECLARE #flag1 INT
DECLARE #flag2 VARCHAR(50)
DECLARE #flag3 DATETIME
DECLARE #idoc INT
exec sp_xml_preparedocument #idoc OUTPUT, #xml
SELECT #flag1 = firstparam, flag2 = secondparam, flag3 = thirdparam
FROM OPENXML(#idoc, '/root', 2) WITH
( firstparam INT, secondparam VARCHAR(50), thirdparam DATETIME) as x
END
exec sptest '<root><firstparam>5</firstparam><secondparam>Joes Bar</secondparam><thirdparam>12/30/2010</thirdparam></root>'
Extend as necessary
Another approach I've seen to passing in params or arrays is to pass in an XML string, dump that to a temporary table/table variable and work with it from that point. Not the easiest when you want to manually run a stored procedure, but it works as a work around to the lack of array/dynamic param support.
I've used a little function to separate a CSV string into a table
That way I could go
SELECT col1, col2
FROM myTable
WHERE myTable.ID IN (SELECT ID FROM dbo.SplitIDs('1,2,3,4,5...'))
My function is below:
CREATE FUNCTION [dbo].[SplitIDs]
(
#IDList varchar(500)
)
RETURNS
#ParsedList table
(
ID int
)
AS
BEGIN
DECLARE #ID varchar(10), #Pos int
SET #IDList = LTRIM(RTRIM(#IDList))+ ','
SET #Pos = CHARINDEX(',', #IDList, 1)
IF REPLACE(#IDList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #ID = LTRIM(RTRIM(LEFT(#IDList, #Pos - 1)))
IF #ID <> ''
BEGIN
INSERT INTO #ParsedList (ID)
VALUES (CAST(#ID AS int)) --Use Appropriate conversion
END
SET #IDList = RIGHT(#IDList, LEN(#IDList) - #Pos)
SET #Pos = CHARINDEX(',', #IDList, 1)
END
END
RETURN
END
I'm sure there are better ways to implement this, this is one way I found online and it works well for what I'm doing. If there are some improvement that can be made please comment.