Processing results from plpgsql function - arrays

I have PostgreSQL function witch returns this json array as text:
[{"seq_no":5796,"start_date":null,"end_date":"2008-09-30 12:32:28","geom_change":"Y"},
{"seq_no":8235,"start_date":"2008-09-30 12:32:28","end_date":"2008-10-02 16:43:24","geom_change":"N"},
{"seq_no":9306,"start_date":"2008-10-02 16:43:24","end_date":"2008-10-02 18:31:09","geom_change":"N"},
{"seq_no":9754,"start_date":"2008-10-02 18:31:09","end_date":"2008-10-07 17:08:25","geom_change":"N"},
{"seq_no":10701,"start_date":"2008-10-07 17:08:25","end_date":"2008-10-08 15:17:48","geom_change":"N"},
{"seq_no":8940,"start_date":"2008-10-08 15:17:48","end_date":"2008-10-08 15:51:47","geom_change":"N"},
{"seq_no":12500,"start_date":"2008-10-08 15:51:47","end_date":"2008-10-08 17:34:35","geom_change":"N"},
{"seq_no":13079,"start_date":"2008-10-08 17:34:35","end_date":"2008-10-08 17:56:03","geom_change":"N"}]
I want to use this data to select seq_no filtered by start/end dates. A preferred result would be table like this.
seq_no start_date end_date geom_change
------------------------------------------------------------------
5796 NULL 2008-09-30 12:32:2 Y
8235 2008-09-30 12:32:28 2008-10-02 16:43:24 N
Or maybe there is simpler way to use this data to select seq_no between start_date and end_date?

You can cast your result as jsonb and then use the jsonb functions and operators along with a tzrange as follows:
with indata (jdata) as (
values (
'[{"seq_no":5796,"start_date":null,"end_date":"2008-09-30 12:32:28","geom_change":"Y"},
{"seq_no":8235,"start_date":"2008-09-30 12:32:28","end_date":"2008-10-02 16:43:24","geom_change":"N"},
{"seq_no":9306,"start_date":"2008-10-02 16:43:24","end_date":"2008-10-02 18:31:09","geom_change":"N"},
{"seq_no":9754,"start_date":"2008-10-02 18:31:09","end_date":"2008-10-07 17:08:25","geom_change":"N"},
{"seq_no":10701,"start_date":"2008-10-07 17:08:25","end_date":"2008-10-08 15:17:48","geom_change":"N"},
{"seq_no":8940,"start_date":"2008-10-08 15:17:48","end_date":"2008-10-08 15:51:47","geom_change":"N"},
{"seq_no":12500,"start_date":"2008-10-08 15:51:47","end_date":"2008-10-08 17:34:35","geom_change":"N"},
{"seq_no":13079,"start_date":"2008-10-08 17:34:35","end_date":"2008-10-08 17:56:03","geom_change":"N"}]'::jsonb
)
)
select (j->>'seq_no')::int as seq_no,
(j->>'start_date')::timestamp as start_date,
(j->>'end_date')::timestamp as end_date,
j->>'geom_change' as geom_change
from indata i
cross join lateral jsonb_array_elements(jdata) as e(j)
where tsrange((j->>'start_date')::timestamp, (j->>'end_date')::timestamp, '[]') #> '2008-09-30 12:32:28'::timestamp;
db<>fiddle here

Related

Return Parts of an Array in Postgres

I have a column (text) in my Postgres DB (v.10) with a JSON format.
As far as i now it's has an array format.
Here is an fiddle example: Fiddle
If table1 = persons and change_type = create then i only want to return the name and firstname concatenated as one field and clear the rest of the text.
Output should be like this:
id table1 did execution_date change_type attr context_data
1 Persons 1 2021-01-01 Create Name [["+","name","Leon Bill"]]
1 Persons 2 2021-01-01 Update Firt_name [["+","cur_nr","12345"],["+","art_cd","1"],["+","name","Leon"],["+","versand_art",null],["+","email",null],["+","firstname","Bill"],["+","code_cd",null]]
1 Users 3 2021-01-01 Create Street [["+","cur_nr","12345"],["+","art_cd","1"],["+","name","Leon"],["+","versand_art",null],["+","email",null],["+","firstname","Bill"],["+","code_cd",null]]
Disassemble json array into SETOF using json_array_elements function, then assemble it back into structure you want.
select m.*
, case
when m.table1 = 'Persons' and m.change_type = 'Create'
then (
select '[["+","name",' || to_json(string_agg(a.value->>2,' ' order by a.value->>1 desc))::text || ']]'
from json_array_elements(m.context_data::json) a
where a.value->>1 in ('name','firstname')
)
else m.context_data
end as context_data
from mutations m
modified fiddle
(Note:
utilization of alphabetical ordering of names of required fields is little bit dirty, explicit order by case could improve readability
resulting json is assembled from string literals as much as possible since you didn't specified if "+" should be taken from any of original array elements
the to_json()::text is just for safety against injection
)

SQL Server: How to remove a key from a Json object

I have a query like (simplified):
SELECT
JSON_QUERY(r.SerializedData, '$.Values') AS [Values]
FROM
<TABLE> r
WHERE ...
The result is like this:
{ "2019":120, "20191":120, "201902":121, "201903":134, "201904":513 }
How can I remove the entries with a key length less then 6.
Result:
{ "201902":121, "201903":134, "201904":513 }
One possible solution is to parse the JSON and generate it again using string manipulations for keys with desired length:
Table:
CREATE TABLE Data (SerializedData nvarchar(max))
INSERT INTO Data (SerializedData)
VALUES (N'{"Values": { "2019":120, "20191":120, "201902":121, "201903":134, "201904":513 }}')
Statement (for SQL Server 2017+):
UPDATE Data
SET SerializedData = JSON_MODIFY(
SerializedData,
'$.Values',
JSON_QUERY(
(
SELECT CONCAT('{', STRING_AGG(CONCAT('"', [key] ,'":', [value]), ','), '}')
FROM OPENJSON(SerializedData, '$.Values') j
WHERE LEN([key]) >= 6
)
)
)
SELECT JSON_QUERY(d.SerializedData, '$.Values') AS [Values]
FROM Data d
Result:
Values
{"201902":121,"201903":134,"201904":513}
Notes:
It's important to note, that JSON_MODIFY() in lax mode deletes the specified key if the new value is NULL and the path points to a JSON object. But, in this specific case (JSON object with variable key names), I prefer the above solution.

MDX: How to Retrieve data Based on Latest Date For Each Student Id

How to get latest count based on date for each id.
Attempt:
SELECT [Measures].[CourseJoinedCount] ON COLUMNS,
NON EMPTY
(
[Course].[CourseName].[CourseName],
[DimDate].[Full Date].[Full Date],
[Student].[StudentId].[StudentId]
)ON ROWS
FROM [RandD]
I think the use of Generate might help. Currently untested but I will try an mock this up against AdvWrks tomorrow to see if it works:
SELECT
NON EMPTY
[Measures].[CourseJoinedCount] ON 0,
Generate(
[Course].[CourseName].[CourseName].MEMBERS
,[Course].[CourseName].CURRENTMEMBER
*Tail(
NonEmpty(
[DimDate].[Full Date].[Full Date].MEMBERS,
[Course].[CourseName].CURRENTMEMBER
)
)
)
*[Student].[StudentId].[StudentId]
ON 1
FROM [RandD];

SQL Filtering on unique events and datetime

I did ask this question before but deleted the post.
Here is my question: I have a table in SQL Server with multiple events in the table, I am trying to filter on the events and the datetime between the events on a certain X min.
I need help with T-SQL queries.
What can use use to do the following (any links will be appreciated):
take event_id and datetime of device, compare to a list of other event_id's to see if there are any other events withing the last 24 min for that device.
What would work for me? Nested queries? Where clause? CTE?
I have tried 6 queries but not getting the result I need, do I use (max) datetime and then compare on event_ID's?
I know this is not much but any help or links would be appreciated....
On Request (Full Query no changes)
1.
SELECT A.[Unit_id]
,A.[TransDate]
,A.[event_id]
,A.[event_msg]
,B.[TransDate]
,B.[event_id]
,B.[event_msg]
FROM
(Select
A.[Unit_id]
,MAX(A.[TransDate]) AS Transdate
,A.[event_id]
,A.[event_msg]
FROM [JammingEvents].[dbo].[EventLogExtended] AS A
WHERE event_id = '345'
GROUP BY A.[Unit_id]
,A.[event_id]
,A.[event_msg]
) AS A
INNER JOIN
(SELECT
B.[Unit_id]
,MAX(B.[TransDate]) AS Transdate
,B.[event_id]
,B.[event_msg]
FROM [JammingEvents].[dbo].[EventLogExtended] AS B
WHERE B.event_id = '985'
GROUP BY B.[Unit_id]
,B.[event_id]
,B.[event_msg]
) AS B
ON
A.Unit_id = B.Unit_id
WHERE (B.TransDate BETWEEN DATEADD(MINUTE,-24,A.Transdate) AND DATEADD(MINUTE,24,A.Transdate))
this works but only if you compare 2 event Id's. the problem is i need to do this on a bulk scale.
2.
SET NOCOUNT ON
GO
SELECT MAX(ELE.Transdate) AS Transdate
,ELE.[Unit_id]
,ELE.[event_id]
,ELE.[event_msg]
,ELE.[Latitude]
,ELE.[Longitude]
,ELE.[date_ack]
,ELE.[user_id_ack]
,ELE.[date_closed]
,ELE.[user_id_closed]
,ELE.[action]
,ELE.[EventSeqNo]
,ELE.[MsgSeqNo]
,ELE.[Speed]
,ELE.[Heading]
,ELE.[Status1]
,ELE.[Status2]
,ELE.[GeoLocation]
,ELE.[Reg_No]
,ELE.[Company]
,ELE.[Fleet_Code]
,ELE.[IgnStatus]
,ELE.[comboActioned]
FROM
[JammingEvents].[dbo].[EventLogExtended] ELE
INNER JOIN
(
SELECT
F.Transdate ,F.[Unit_id] ,F.[event_id] ,F.[event_msg] ,F.[Latitude] ,F.[Longitude] ,F.[date_ack] ,F.[user_id_ack]
,F.[date_closed] ,F.[user_id_closed] ,F.[action] ,F.[EventSeqNo] ,F.[MsgSeqNo] ,F.[Speed] ,F.[Heading] ,F.[Status1]
,F.[Status2] ,F.[GeoLocation] ,F.[Reg_No] ,F.[Company] ,F.[Fleet_Code] ,F.[IgnStatus] ,F.[comboActioned]
FROM [JammingEvents].[dbo].[EventLogExtended] AS F
WHERE
F.event_id IN
('302'
,'303'
,'304'
,'305'
,'309'
,'340'
,'341'
,'345'
,'962'
,'963'
,'973'
,'974'
,'975'
,'976'
,'985'
,'987'
,'989'
,'C220'
,'C222'
,'C224'
,'C227'
,'C228')
) AS A
ON
ELE.Unit_id = A.Unit_id
INNER JOIN
(
SELECT
G.Transdate ,G.[Unit_id] ,G.[event_id] ,G.[event_msg] ,G.[Latitude] ,G.[Longitude] ,G.[date_ack]
,G.[user_id_ack] ,G.[date_closed] ,G.[user_id_closed] ,G.[action] ,G.[EventSeqNo] ,G.[MsgSeqNo] ,G.[Speed] ,G.[Heading]
,G.[Status1] ,G.[Status2] ,G.[GeoLocation] ,G.[Reg_No] ,G.[Company] ,G.[Fleet_Code] ,G.[IgnStatus] ,G.[comboActioned]
FROM [JammingEvents].[dbo].[EventLogExtended] AS G
WHERE
G.event_id = '345'
) AS B
ON
A.Unit_id = B.Unit_id
WHERE (ELE.TransDate BETWEEN DATEADD(MINUTE,-24,A.Transdate) AND DATEADD(MINUTE,24,A.Transdate))
AND
(ELE.event_id IN
(
'302'
,'303'
,'304'
,'305'
,'309'
,'340'
,'341'
,'345'
,'962'
,'963'
,'973'
,'974'
,'975'
,'976'
,'985'
,'987'
,'989'
,'C220'
,'C222'
,'C224'
,'C227'
,'C228'
))
AND
(ELE.action = '0')
GROUP BY ELE.TransDate
,A.Unit_id
,ELE.[Unit_id]
,ELE.[event_id]
,ELE.[event_msg]
,ELE.[Latitude]
,ELE.[Longitude]
,ELE.[date_ack]
,ELE.[user_id_ack]
,ELE.[date_closed]
,ELE.[user_id_closed]
,ELE.[action]
,ELE.[EventSeqNo]
,ELE.[MsgSeqNo]
,ELE.[Speed]
,ELE.[Heading]
,ELE.[Status1]
,ELE.[Status2]
,ELE.[GeoLocation]
,ELE.[Reg_No]
,ELE.[Company]
,ELE.[Fleet_Code]
,ELE.[IgnStatus]
,ELE.[comboActioned]
ORDER BY ELE.TransDate, ELE.Unit_id DESC
so far this got me the closest but i have no idea what to do, i am very poor with SQL syntax and writing queries.
You could use an exists subquery to demand that there was another event for the same device within the preceeding 24 minutes:
select *
from Events e1
where exists
(
select *
from Events e2
where e2.Device_ID = e1.Device_ID
and e2.event_dt between
dateadd(minute, e1.event_dt, -24)
and e1.event_dt
)
Self join should work as well:
SELECT *, DATEDIFF(minute,b.transdate,a.transdate) diff FROM EventLogWxtended a
JOIN eventLogExtended b
ON (a.unit_id=b.unit_id AND b.transdate<a.transdate
AND DATEDIFF(minute,b.transdate,a.transdate)<24)

case statement filter in MDX query

I want to write the following T SQL query in MDX
Select count(bugs),priority from table
where
Case when priority =1 then startdate< dateadd(dd,-7,getdate())
when priority =2 then startdate< dateadd(dd,-14,getdate())
end
group by priority
Tried the following but not working
WITH MEMBER [Measures].CHECKING
AS
CASE [Item].[ startdate].CurrentMember
WHEN [Item].[ Priority].&[1] THEN [Item].[startdate]<DATEADD(DAY,-7,NOW())
WHEN [Item].[ Priority].&[2] THEN [Item].[startdate]<DATEADD(DAY,-14,NOW())
END
SELECT
NON EMPTY{[Measures].[Count], [Measures].CHECKING }ON COLUMNS
,NON EMPTY{([Item].[ Priority].[ Priority].ALLMEMBERS )}
I am new to MDX queries, any suggestions on how to approach this please..
Your CASE logic has a basic problem. The statement cannot result in a condition. It can only result in a value that you then compare to something else.
To take your tSQL example, I think it should read more like this:
Select count(bugs),priority from table
where
1 = Case when priority = 1 and startdate< dateadd(dd,-7,getdate()) Then 1
when priority = 2 and startdate< dateadd(dd,-14,getdate()) then 1
else 0 end
group by priority
A cleaner way to write this would be to skip the CASE altogether.
Select count(bugs),priority from table
where
(priority = 1 and startdate< dateadd(dd,-7,getdate()))
or
(priority = 2 and startdate< dateadd(dd,-14,getdate()))
group by priority
I am assuming the following:
Your startdate hierarchy is an attribute hierarchy, not a user hierarchy and
the current day is its last member.
Then the following MDX should deliver what you want:
SELECT
{ [Measures].[Count] }
ON COLUMNS
,
{ [Item].[ Priority].&[1], [Item].[ Priority].&[2] }
ON ROWS
FROM (
SELECT ({ [Item].[ Priority].&[1] }
*
([Item].[ startdate].[ startdate].Members
- Tail([Item].[ startdate].[ startdate].Members, 7)
)
)
+
({ [Item].[ Priority].&[2] }
*
([Item].[ startdate].[ startdate].Members
- Tail([Item].[ startdate].[ startdate].Members, 14)
)
)
ON COLUMNS
FROM [yourCube]
)
Your code [Item].[startdate]<DATEADD(DAY,-7,NOW()) does not work in MDX for several reasons: firstly, [Item].[startdate] is a hierarchy, and hence cannot be compared using <. Secondly, even if you would re-state it as [Item].[startdate].CurrentMember < DATEADD(DAY,-7,NOW()), you would have a member on the left side of the <, and a date , i. e. a value, on the right side. One of the important things to keep in mind with MDX are the different types of objects: Hierarchies, levels, members, tuples, sets. And all these are not values. You do not just have columns like in SQL.

Resources