Find local minima/maxima - sqlite

I need a query to update the value at the point from which the metrics rises or decreases .For example I have a table with
ID METRICS INDICATOR
1 204.4
2 205
3 206 H
4 204
5 199
6 198 L
7 204
8 205 H
9 201
10 199
If you see the above table the metrics column the reversal of metrics happens . The point the reversal happens should be updated with the indicator value H/L as shown in the indicator column.

You want a "H" when both the preceding and the following rows have smaller values:
UPDATE MyTable
SET Indicator = 'H'
WHERE Metrics > (SELECT Metrics
FROM MyTable AS T2
WHERE T2.ID < MyTable.ID
ORDER BY ID DESC
LIMIT 1)
AND Metrics > (SELECT Metrics
FROM MyTable AS T2
WHERE T2.ID > MyTable.ID
ORDER BY ID ASC
LIMIT 1);
You want a "L" when both the preceding and the following rows have larger values; use a similar query.

Related

SQLITE get next row after ORDERBY

I need to get the next row from an ORDERBY query
I have 2 columns, ID(Primary key), Age(float) in a table T and I need something like the following
SELECT ID FROM T WHERE !> (inputted ID) + 1 rowID/Next row <! ORDERBY Age (then primary key, but I suspect if the Age values are the same SQLite would default to order by primary key anyway) LIMIT 1
Essentially it would select the next row after the inputted ID in the ordered table, its the next row / rowID + 1 I am not sure how to get.
As suggested here is a data set as an example
https://dbfiddle.uk?rdbms=sqlite_3.27&fiddle=19685ac20cc42041a59d318a01a2010f
ID Age
1 12.2
2 36.8
3 22.5
4 41
5 16.7
I am attempting to get the the following row from the ordered (by age) list given a specific ID
ID Age
1 12.2
5 16.7
3 22.5
2 36.8
4 41
Something similar to
SELECT ID FROM OrderedInfo WHERE ID = 5 ORDER BY Age ASC LIMIT 1 OFFSET 1;
My expected result would be '3' from the example data above
I have expanded the data set to include duplicate entries as I didn't implicitly state it could have such data - as such forpas answer works for the first example with no duplicate entries - thanks for your help
https://dbfiddle.uk?rdbms=sqlite_3.27&fiddle=f13d7f5a44ba414784547d9bbdf4997e
Use a subquery for the ID that you want in the WHERE clause:
SELECT *
FROM OrderedInfo
WHERE Age > (SELECT Age FROM OrderedInfo WHERE ID = 5)
ORDER BY Age LIMIT 1;
See the demo.
If there are duplicate values in the column Age use a CTE that returns the row that you want and join it to the table so that you expand the conditions:
WITH cte AS (SELECT ID, Age FROM OrderedInfo WHERE ID = 5)
SELECT o.*
FROM OrderedInfo o INNER JOIN cte c
ON o.Age > c.Age OR (o.Age = c.Age AND o.ID > c.ID)
ORDER BY o.Age, o.ID LIMIT 1;
See the demo.

Retain values only for certain section of data in teradata

Below is the link of my previous quetsion.
Retain values till there is a change in value in Teradata
It worked as suggested by one of the community members #Dnoeth. Can this retention be done only for certain section of data?
I.e, Retain data only for data where Dep is A or B . When Dep is C just use same value as input and no need to retain till certain value.
Data:
Cust_id Balance st_ts Dep
123 1000 27MAY2018 A
123 350 31MAY2018 A
256 2000 29MAY2018 B
345 1000 28APR2018 C
345 1200 26MAY2018 C
Output reqd:
Cust_id Balance st_ts Dep
123 1000 27MAY2018 A
123 1000 28MAY2018 A
123 1000 29MAY2018 A
123 1000 30MAY2018 A
123 350 31MAY2018 A
256 2000 29MAY2018 B
256 2000 30MAY2018 B
256 2000 31MAY2018 B
345 1000 28APR2018 C
345 1200 26MAY2018 C
Query used:
Wth cte
{
SELECT customer_id, bal, st_ts,
-- return the next row's date
Coalesce(Min(st_ts)
Over (PARTITION BY customer_id
ORDER BY st_ts
ROWS BETWEEN 1 Following AND 1 Following)
,Date '2018-06-01') AS next_Txn_dt
FROM BAL_DET;
}
SELECT customer_id, bal
,Last(pd) -- last day of the period
FROM cTE
-- make a period of the current and next row's date
-- and return one row per day
EXPAND ON PERIOD(ST_TS, next_Txn_dt) AS pd;
Thanks
Sandy
You can add a CASE to check for Dep = 'C':
WITH cte AS
( SELECT customer_id, bal, st_ts, dep,
-- return the next row's date
CASE
WHEN dep = 'C'
THEN st_ts +1 -- simply increase date
ELSE
Coalesce(Min(st_ts)
Over (PARTITION BY customer_id
ORDER BY st_ts
ROWS BETWEEN 1 Following AND 1 Following)
,DATE '2018-06-01')
END AS next_Txn_dt
FROM BAL_DET
)
SELECT customer_id, bal
,Last(pd) -- last day of the period
,dep
FROM cTE
-- make a period of the current and next row's date
-- and return one row per day
EXPAND ON PERIOD(ST_TS, next_Txn_dt) AS pd

Creating even ranges based on values in an oracle table

I have a big table which is 100k rows in size and the PRIMARY KEY is of the datatype NUMBER. The way data is populated in this column is using a random number generator.
So my question is, can there be a possibility to have a SQL query that can help me with getting partition the table evenly with the range of values. Eg: If my column value is like this:
1
2
3
4
5
6
7
8
9
10
And I would like this to be broken into three partitions, then I would expect an output like this:
Range 1 1-3
Range 2 4-7
Range 3 8-10
It sounds like you want the WIDTH_BUCKET() function. Find out more.
This query will give you the start and end range for a table of 1250 rows split into 20 buckets based on id:
with bkt as (
select id
, width_bucket(id, 1, 1251, 20) as id_bucket
from t23
)
select id_bucket
, min(id) as bkt_start
, max(id) as bkt_end
, count(*)
from bkt
group by id_bucket
order by 1
;
The two middle parameters specify min and max values; the last parameter specifies the number of buckets. The output is the rows between the minimum and maximum bows split as evenly as possible into the specified number of buckets. Be careful with the min and max parameters; I've found poorly chosen bounds can have an odd effect on the split.
This solution works without width_bucket function. While it is more verbose and certainly less efficient it will split the data as evenly as possible, even if some ID values are missing.
CREATE TABLE t AS
SELECT rownum AS id
FROM dual
CONNECT BY level <= 10;
WITH
data AS (
SELECT id, rownum as row_num
FROM t
),
total AS (
SELECT count(*) AS total_rows
FROM data
),
parts AS (
SELECT rownum as part_no, total.total_rows, total.total_rows / 3 as part_rows
FROM dual, total
CONNECT BY level <= 3
),
bounds AS (
SELECT parts.part_no,
parts.total_rows,
parts.part_rows,
COALESCE(LAG(data.row_num) OVER (ORDER BY parts.part_no) + 1, 1) AS start_row_num,
data.row_num AS end_row_num
FROM data
JOIN parts
ON data.row_num = ROUND(parts.part_no * parts.part_rows, 0)
)
SELECT bounds.part_no, d1.ID AS start_id, d2.ID AS end_id
FROM bounds
JOIN data d1
ON d1.row_num = bounds.start_row_num
JOIN data d2
ON d2.row_num = bounds.end_row_num
ORDER BY bounds.part_no;
PART_NO START_ID END_ID
---------- ---------- ----------
1 1 3
2 4 7
3 8 10

sql group by with count and add totals row

So my situation. Got a table with few thousand entries, I took one column and counted the number of the same entries.
SELECT mycolumn, COUNT(*)
FROM mytable
WHERE myid = 6867
GROUP BY mycolumn
ORDER BY COUNT(*) DESC
Outputs:
6 885
1 715
4 562
5 557
2 232
3 181
I get the desired results. Now all I need is to add a bottom row with a sum of all counted entries.
Typically you would do this in some reporting tool, like SSRS, not SQL.
To do it in SQL, add a UNION statement:
UNION ALL
SELECT 0, COUNT(*)
FROM mytable
WHERE myid = 6867;

SQLite Query to group continuous range of numbers into different grouping sets

I want get the islands of this table below:
Group MemberNo
A 100
A 101
A 200
A 201
A 202
A 203
X 100
X 101
A 204
X 301
X 302
A 500
A 600
I want get this results using SQL (the islands):
Group FromMemberNo ToMemberNo
A 100 101
A 200 204
X 100 101
X 301 302
A 500 500
A 600 600
I have seen a lot of codes/forums for this but not working with SQLite because SQLite doesn't have CTEs.
100-101 is continuous so that it will be group into one.
Does anyone know how to do it in SQLite?
The fastest way to do this would be to go through the ordered records of this table in a loop and collect the islands manually.
In pure SQL (as a set-oriented language), this is not so easy.
First, we find out which records are the first in an island. The first record does not have a previous record, i.e., a record with the same group but with a MemberNo one smaller:
SELECT "Group",
MemberNo AS FromMemberNo
FROM ThisTable AS t1
WHERE NOT EXISTS (SELECT 1
FROM ThisTable AS t2
WHERE t2."Group" = t1."Group"
AND t2.MemberNo = t1.MemberNo - 1)
To find the last record of an island, we have to find the record with the largest MemberNo that still belongs to the same island, i.e., has the same group, and where all MemberNos in the island are continuous.
We detect continuous MemberNos by computing the difference between their values in the first and last records.
The last MemberNo of the island with group G and first MemberNo M can be computed like this:
SELECT MAX(MemberNo) AS LastMemberNo
FROM ThisTable AS t3
WHERE t3."Group" = G
AND t3.MemberNo - M + 1 = (SELECT COUNT(*)
FROM ThisTable AS t4
WHERE t4."Group" = G
AND t4.MemberNo BETWEEN M AND t3.MemberNo)
Finally, plug this into the first query:
SELECT "Group",
MemberNo AS FromMemberNo,
(SELECT MAX(MemberNo)
FROM ThisTable AS t3
WHERE t3."Group" = t1."Group"
AND t3.MemberNo - t1.MemberNo + 1 = (SELECT COUNT(*)
FROM ThisTable AS t4
WHERE t4."Group" = t1."Group"
AND t4.MemberNo BETWEEN t1.MemberNo AND t3.MemberNo)
) AS LastMemberNo
FROM ThisTable AS t1
WHERE NOT EXISTS (SELECT 1
FROM ThisTable AS t2
WHERE t2."Group" = t1."Group"
AND t2.MemberNo = t1.MemberNo - 1)

Resources