SQLite ignoring calculation used in with a WHERE statement - sqlite

I'm trying to use an SQLite query to calculate when an alarm should become active. I store the start time and the detent (the timeout or delay before the alarm becomes active) in the same table as seconds. Here is the schema:
CREATE TABLE alarm_log (
scope integer NOT NULL, --REFERENCES scopes_inst(inst)
start integer NOT NULL, -- seconds
end integer,
severity text NOT NULL,
value text NOT NULL,
details text,
raised_by integer, -- the scope id that raised the alarm
detent integer, -- detent in seconds
PRIMARY KEY (scope, start)
);
And here is the query that I am trying to use to pull the active alarms using some simple math:
SELECT *,
(start + detent) AS activated, -- the time that the alarm becomes active
STRFTIME("%s","now") AS now -- the current time of the query
FROM alarm_log
WHERE end IS NULL
AND now > activated; -- ensure that the alarm is activated
I have created an alarm with a detent of 30 seconds. The start time is 1378870712 so it should become active at 1378870742 (see the activated column).
This is an example of the above query returning rows 4 seconds before the activated time...
scope start end severity value details raised_by detent activated now
---------- ---------- ---------- ---------- --------------------- ---------------- ---------- ---------- ---------- ----------
4 1378870712 warning out-of-range too-high min 500 max 1500 0 30 1378870742 1378870738
Now the same query returns that row 2 seconds after the alarm was activated.
scope start end severity value details raised_by detent activated now
---------- ---------- ---------- ---------- --------------------- ---------------- ---------- ---------- ---------- ----------
4 1378870712 warning out-of-range too-high min 500 max 1500 0 30 1378870742 1378870744
I would expect reversing the > on the last line to a < it should show alarms that are not active... but this returns nothing, before or after a detent time passes...
Is this normal behaviour? The query looks fine to me. Thanks for reading :)
EDIT:
I should have mentioned that I also tried it with the calculations straight in the WHERE clause, but it does the same thing :(
SELECT *,
(start + detent) AS activated, -- the time that the alarm becomes active
STRFTIME("%s","now") AS now -- the current time of the query
FROM alarm_log
WHERE end IS NULL
AND STRFTIME("%s","now") > (start + detent);
EDIT2:
Here's some test data:
SQLite styles:
scope|start|end|severity|value|details|raised_by|detent
4|1378935271|1378935842|warning|out-of-range too-high|min 500 max 1500|0|600
4|1378935854|1378935876|warning|out-of-range too-high|min 500 max 1500|0|600
4|1378935884||warning|out-of-range too-high|min 500 max 1500|0|600
CSV styles
scope,start,end,severity,value,details,raised_by,detent
4,1378935271,1378935842,warning,out-of-range too-high,min 500 max 1500,0,600
4,1378935854,1378935876,warning,out-of-range too-high,min 500 max 1500,0,600
4,1378935884,,warning,out-of-range too-high,min 500 max 1500,0,600

Try this:
select * from (
select *, (start + detent) as activated,
strftime("%s","now") as now
from alarm_log where end is null
) where cast(now as int) > activated;

Related

InnoDB: Why does transaction wait for IX lock when it already has a X lock?

I'm getting a deadlock in my code. Txn 1 is waiting for a lock to be granted which is held currently by Txn 2. Txn 2 already has a X lock but still is requesting for a IX lock.
Both the transactions run an Insert query using ActiveRecord import.
The deadlock section in SHOW ENGINE INNODB STATUS gives me:
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147861 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
My question is why does Txn 2 need an IX lock if it already holds an X lock?
Update 1:
Here is the complete LATEST DETECTED DEADLOCK section:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-09-02 16:11:22 0x149b2c2ef700
*** (1) TRANSACTION:
TRANSACTION 51241147861, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 8 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1
MySQL thread id 8423321, OS thread handle 22689954051840, query id 19045173171 172.31.15.180 mitdb4dm1n update
INSERT INTO `user_metrics` (`id`,`current`,`total`,`my_type`,`owner_id`,`owner_type`,`created_at`,`updated_at`) VALUES (NULL,175,175,0,108840,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,100,151,0,108841,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,169,169,0,112780,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,202,217,0,112781,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,26,62,0,112782,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,169,169,0,112794,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,177,217,0,112795,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,28,62,0,112796,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,140,140,0,114162,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,64,64,0,114163,'OwnerName','2022-09-0
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147861 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 51241147863, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
7 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1
MySQL thread id 8424694, OS thread handle 22656693761792, query id 19045173179 172.31.4.27 mitdb4dm1n update
INSERT INTO `user_metrics` (`id`,`current`,`total`,`my_type`,`owner_id`,`owner_type`,`created_at`,`updated_at`) VALUES (NULL,1,89,0,137623,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,0,3,0,137624,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,178,182,0,137635,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,77,129,0,137645,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,5,14,0,137646,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,0,87,0,137656,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,0,11,0,137657,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,71,71,0,146601,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,71,71,0,146616,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,39,64,0,146631,'OwnerName','2022-09-02 16:11:22','2022-0
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
Update 2:
Create table output for the table involved:
CREATE TABLE `user_metrics` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`current` decimal(10,0) DEFAULT NULL,
`total` decimal(10,0) DEFAULT NULL,
`my_type` int(11) DEFAULT NULL,
`owner_id` int(11) DEFAULT NULL,
`owner_type` varchar(255) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_user_metrics_on_owner_type_and_owner_id_and_my_type` (`owner_type`,`owner_id`,`my_type`)
) ENGINE=InnoDB AUTO_INCREMENT=1167 DEFAULT CHARSET=utf8mb4
Plan A:
Live with it. But do catch the error and replay the INSERT.
Plan B:
Try this. (I have no confidence that it will help):
You have two Unique keys (the PK is one). Let's switch them around, to the following. (It assumes you can change all three columns to NOT NULL.)
PRIMARY KEY(`owner_type`,`owner_id`,`my_type`),
INDEX(id)
Rationale:
Two Unique keys leads to two things being locked, and more than twice the likelihood of a conflict.
Having the data clustered in the order that is beneficial to the query will speed up the query, hence making it more likely to finish before conflicting with another connection.
I doubt if either of these will be sufficient to prevent deadlocks. But they may decrease the frequency of deadlocks. Hence, plan doing Plan A, too.

Select record conditionally by comparing date and time

Using SQLite, how do I retrieve the first record where current time is between record start and stop times, OR ELSE, if current time is outside start/stop times of all existing records, instead retrieve the record that comes next in time after current local time?
Here's my table:
TABLE sessions (
session_id INTEGER PRIMARY KEY AUTOINCREMENT,
session_start REAL,
session_stop REAL,
group_id INTEGER REFERENCES [groups] (group_id) ON DELETE CASCADE
);
Example data:
session_id session_start session_stop group_id
1 2459599.755438762 2459599.963772095 1
2 2459600.0471054283 2459600.1721054283 2
3 2459600.755438762 2459600.963772095 1
The session table has a foreign key, group_id, but for sake of the example maybe we can leave it aside.
EDIT:
Tried mankowitz suggestion within a small test application:
SQL.Text:='SELECT *, julianday(''Now'',''localtime'') AS julian_now FROM sessions '
+'WHERE session_start > julian_now '
+'ORDER BY session_stop < julian_now LIMIT 1';
Current time at runtime was 2022-01-17 05:40:45 and the record with session_id 16 should have been selected since it is the closest to in time. How to modify the code for this?
For the condition:
current time is between record start and stop times
you need this boolean expression in the ORDER BY clause:
julianday('now', 'localtime') BETWEEN session_start AND session_stop
and for the condition:
OR ELSE, if current time is outside start/stop times of all existing
records, instead retrieve the record that comes next in time after
current local time
you need this:
julianday('now', 'localtime') > session_stop
Write your query like this:
SELECT *
FROM sessions
ORDER BY julianday('now', 'localtime') BETWEEN session_start AND session_stop DESC,
julianday('now', 'localtime') > session_stop DESC,
session_start
LIMIT 1;
select * from sessions where sesson_start>{currenttime}
order by session_end<{currenttime}
limit 1

How do I reset a sequence in Oracle APEX or fill my PK automatically without sequence and trigger, starting from number 1 every time i delete my data?

i have this table
TABLE "KEYWORD_RSLT"
( "ID" NUMBER PRIMARY KEY,
"SESSION_MONTH" VARCHAR2(40) NOT NULL ENABLE,
"PATIENT_NAME" VARCHAR2(50)
and i have this procedure that imports data to my table KEYWORD_RSLT.
create or replace PROCEDURE "PR_KEYWORD_SEARCH" (v_patient_id NUMBER, v_keyWord varchar2)
IS
BEGIN
delete from KEYWORD_RSLT;
insert into KEYWORD_RSLT (SESSION_MONTH, PATIENT_NAME)
select distinct
to_char(s.SESSION_DATE, 'MM-YYYY') as SESSION_MONTH,
p.FIRST_NAME ||' '||p.LAST_NAME as PATIENT_NAME
from SESSIONS s,
CLIENTS p
where s.CLIENTS_ID = p.ID
and (s.CRITICAL_POINT like LOWER ('%'||v_keyWord||'%') and s.CLIENTS_ID = v_patient_id
or s.ACTIONS like LOWER ('%'||v_keyWord||'%') and s.CLIENTS_ID = v_patient_id);
END PR_KEYWORD_SEARCH;
I want my primary key "ID" to take automatically the next available number starting from 1, but when my procedure deletes all data from this table, i want to start again from 1.
I tried with sequence ("SQ_KEYWORD_RSLT_INCREAMENT") and trigger but i can not reset this sequence from a new procedure using this code:
alter sequence SQ_KEYWORD_RSLT_INCREAMENT restart start with 1;
How can i fill my ID automatically from the beginning every time i delete all the data?
You say i can not reset this sequence but you don't say why so I'm assuming that you got an error. It is not possible to execute ddl statements in pl/sql directly, but it can be done using EXECUTE IMMEDIATE.
CREATE SEQUENCE koen_s START WITH 1;
Sequence KOEN_S created.
SELECT koen_s.NEXTVAL FROM DUAL;
NEXTVAL
----------
1
BEGIN
EXECUTE IMMEDIATE 'ALTER SEQUENCE koen_s RESTART START WITH 1';
END;
/
PL/SQL procedure successfully completed.
SELECT koen_s.NEXTVAL FROM DUAL;
NEXTVAL
----------
1

oracle 11g application express interface virtual column that calculates total amount of sales

Im currently working on my interface project by editing a certain page item of a form in oracle 11g application express. Im in confusion that one of the column is derived from another column in the table, does not work properly when i tried to register a new data through the interface. the column cannot calculate the derived data just like how it works in the oracle sql developer. i set the column as follows:
Im clueless, that should i display the column as hidden, or it has something to do with the source settings that needs PL/SQL expression to calculate the values of quantity, and cost_perunit column for total_amount values automatically. I have searched the web for the solution, but cant find a solution and a related issue to this.
this command was uploaded in a text .txt format into the application express
CREATE TABLE PAYMENT(
PAY_ID NUMBER(25)NOT NULL,
PAY_DATE DATE,
PAY_METHOD VARCHAR2(50 char),
SPARE_TYPE VARCHAR2(50 char),
QUANTITY NUMBER(12),
COST_PERUNIT NUMBER(6,2),
TOTAL_AMOUNT as (quantity*cost_perunit),
CONSTRAINT PAY_PK PRIMARY KEY(PAY_ID)
);
CREATE SEQUENCE pay_id_seq START WITH 400;
Im referring to the TOTAL_AMOUNT as (quantity*cost_perunit) column that returns error when i try to create a new data through the form. what should i change in the page edit settings, so it will work as it supposed to be.
You have two choices create a table without virtual column calculate total amount at run time
e.g select a.*,(quantity*cost_perunit) total_amount from payment a;
or create table with virtual column with keyword virtual
CREATE TABLE PAYMENT(
PAY_ID NUMBER(25)NOT NULL,
PAY_DATE DATE,
PAY_METHOD VARCHAR2(50 char),
SPARE_TYPE VARCHAR2(50 char),
QUANTITY NUMBER(12),
COST_PERUNIT NUMBER(6,2),
TOTAL_AMOUNT as (quantity*cost_perunit) VIRTUAL,
CONSTRAINT PAY_PK PRIMARY KEY(PAY_ID));
#ddl payment
DBMS_METADATA.GET_DDL(OBJECT_TYPE,OBJECT_NAME,OWNER)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE TABLE "SCOTT"."PAYMENT"
( "PAY_ID" NUMBER(25,0) NOT NULL ENABLE,
"PAY_DATE" DATE,
"PAY_METHOD" VARCHAR2(50 CHAR),
"SPARE_TYPE" VARCHAR2(50 CHAR),
"QUANTITY" NUMBER(12,0),
"COST_PERUNIT" NUMBER(6,2),
"TOTAL_AMOUNT" NUMBER GENERATED ALWAYS AS ("QUANTITY"*"COST_PERUNIT") VIRTUAL ,
CONSTRAINT "PAY_PK" PRIMARY KEY ("PAY_ID")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
TABLESPACE "EXAMPLE" ENABLE
) SEGMENT CREATION DEFERRED
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
TABLESPACE "EXAMPLE" ;
SQL> insert into payment (pay_id,pay_date,pay_method,spare_type,quantity,cost_perunit)
2 values
3 (&pid,&pdt,&pmethod,&spare,&qty,&cpu);
Enter value for pid: 101
Enter value for pdt: sysdate
Enter value for pmethod: 'CASH'
Enter value for spare: 'CREDIT CARD'
Enter value for qty: 12
Enter value for cpu: 1.9
1 row created.
Elapsed: 00:00:00.10
SQL> /
Enter value for pid: 102
Enter value for pdt: sysdate-1
Enter value for pmethod: 'DEBIT CARD'
Enter value for spare: 'CREDIT CARD'
Enter value for qty: 21
Enter value for cpu: 2.99
1 row created.
Elapsed: 00:00:00.00
SQL> commit;
Commit complete.
SQL> select * from payment;
PAY_ID PAY_DATE PAY_METHOD SPARE_TYPE QUANTITY COST_PERUNIT TOTAL_AMOUNT
---------- ------------------- -------------------------------------------------- -------------------------------------------------- ---------- ------------ ------------
101 2020-06-25 14:06:46 CASH CREDIT CARD 12 1.9 22.8
102 2020-06-24 14:07:29 DEBIT CARD CREDIT CARD 21 2.99 62.79
SQL>
Edit:I have no idea about apex but if you decide to create the table without virtual column you can add label box or non editable text box item and use formula to display calculation for apex forms run time

Get two records from PLSQL cursor

My question is short. I create a cursor to get some values from my table. I want to get the current record of cursor (the fetched record) and the next record from the cursor without fetching it, because I want to make a calculation over the current record and the next record. In traditional programming it's a simple operation; you can do it with a for index by increasing it by 1.
Do you have any suggestions?
I want to make a calculation over the
current record and the next record.
Presuming you are using Oracle, this can be done quite simply in SQL using analtyical functions, specifically the lead() function. This retrieves a column's value from the next nth record (default n=1).
SQL> select empno
2 , sal as this_sal
3 , lead(sal) over (order by empno) as next_sal
4 from emp
5 order by empno
6 /
EMPNO THIS_SAL NEXT_SAL
---------- ---------- ----------
7369 800 1600
7499 1600 1250
7521 1250 2975
7566 2975 1250
7654 1250 2850
7698 2850 2450
....
This query can be using in a cursor or any other mechanism for retrieving records.
I'm not sure you can do that without moving the cursor, but you should be able to accomplish the same goal like this (psuedocode):
open cursor;
fetch cursor to rec1;
if cursor%found then
loop
fetch cursor to rec2;
exit when cursor%notfound;
perform calculations with rec1 and rec2;
rec1 := rec2;
end loop;
end if;
To answer you specific question, you could use BULK COLLECT and its limit clause into a collection (table) variable.
DECLARE
CURSOR my_cursor IS
[YOUR SQL HERE];
TYPE t_my_type IS TABLE OF my_cursor%ROWTYPE INDEX BY BINARY_INTEGER;
v_my_var t_my_type;
BEGIN
FETCH my_cursor BULK COLLECT INTO v_my_var LIMIT 2;
dbms_output.put_line('My first value: ' || v_my_var(1).some_column);
dbms_output.put_line('My second value: ' || v_my_var(2).some_column);
END;
The limit clause will cause only the first two records to be fetched and stored. The first record will have an index of 1 and the second will be 2.
You can compare two variables by saving one into local and then compare it with cursor value for example
declare
l_local1 table_name%type := null;
begin
for c1 in (select
*
from
table_name
where some clause)
loop
-- do comparation
if l_local1 is not null and c1.field_value is not null then
-- do something to compare
.
.
.
-- empty local1 variable
l_local1 := null;
end if;
-- get the value of loop in local variable
l_local1 := c1.field_value;
end loop;
end;

Resources