python+cx_oracle can not parse "connect by" syntax - python-3.6

I wonder why my query result is different between oracle client (I use DBeaver 7.1) and python+cx_oracle when I use connect by syntax to get date list of TUESDAY in current month, belows are my code, any help will be thankful.
oracle
SELECT
ROWNUM AS ID,
TO_CHAR(TRUNC(DT, 'MM') + LEVEL - 1, 'YYYYMMDD') AS MDATE
FROM (SELECT TO_DATE(SYSDATE) dt FROM dual)
WHERE TO_CHAR(TRUNC(DT, 'MM') + LEVEL - 1, 'DY') IN ('TUE')
CONNECT BY TRUNC(TRUNC(DT, 'MM') + LEVEL - 1, 'MM') = TRUNC(DT, 'MM')
the result is:
| ID | MDATE |
|----|----------|
| 1 | 20200707 |
| 2 | 20200714 |
| 3 | 20200721 |
| 4 | 20200728 |
python
In[1]: import cx_Oracle
In[2]: cursor = cx_Oracle.connection('id', 'pw', 'port', 'dsn').cursor()
In[3]: sql = """
SELECT
ROWNUM AS ID,
TO_CHAR(TRUNC(DT, 'MM') + LEVEL - 1, 'YYYYMMDD') AS MDATE
FROM (SELECT TO_DATE(SYSDATE) dt FROM dual)
WHERE TO_CHAR(TRUNC(DT, 'MM') + LEVEL - 1, 'DY') IN ('TUE')
CONNECT BY TRUNC(TRUNC(DT, 'MM') + LEVEL - 1, 'MM') = TRUNC(DT, 'MM')
"""
In[4]: cursor.execute(sql).fetchall()
Out[4]: []
requirements
cx-Oracle==7.3.0
python=3.6.8

Related

SQLite: Calculate how a counter has increased in current day and week

I have a SQLite database with a counter and timestamp in unixtime as showed below:
+---------+------------+
| counter | timestamp |
+---------+------------+
| | 1582933500 |
| 1 | |
+---------+------------+
| 2 | 1582933800 |
+---------+------------+
| ... | ... |
+---------+------------+
I would like to calculate how 'counter' has increased in current day and current week.
It is possible in a SQLite query?
Thanks!
Provided you have SQLite version >= 3.25.0 the SQLite window functions will help you achieve this.
Using the LAG function to retrieve the value from the previous record - if there is none (which will be the case for the first row) a default value is provided, that is same as current row.
For the purpose of demonstration this code:
SELECT counter, timestamp,
LAG (timestamp, 1, timestamp) OVER (ORDER BY counter) AS previous_timestamp,
(timestamp - LAG (timestamp, 1, timestamp) OVER (ORDER BY counter)) AS diff
FROM your_table
ORDER BY counter ASC
will give this result:
1 1582933500 1582933500 0
2 1582933800 1582933500 300
In a CTE get the min and max timestamp for each day and join it twice to the table:
with cte as (
select date(timestamp, 'unixepoch', 'localtime') day,
min(timestamp) mindate, max(timestamp) maxdate
from tablename
group by day
)
select c.day, t2.counter - t1.counter difference
from cte c
inner join tablename t1 on t1.timestamp = c.mindate
inner join tablename t2 on t2.timestamp = c.maxdate;
With similar code get the results for each week:
with cte as (
select strftime('%W', date(timestamp, 'unixepoch', 'localtime')) week,
min(timestamp) mindate, max(timestamp) maxdate
from tablename
group by week
)
select c.week, t2.counter - t1.counter difference
from cte c
inner join tablename t1 on t1.timestamp = c.mindate
inner join tablename t2 on t2.timestamp = c.maxdate;

How to get nearest DateTime from 2 tables

In SQLite, I want to build a query to get the nearest datetime for 'tag' entries against a 'tick' list:
CREATE TABLE Tick (
id integer primary key,
dt varchar(20)
);
INSERT INTO Tick (id, dt) VALUES
( 1, '2018-10-30 13:00:00'),
( 2, '2018-10-30 14:00:00'),
( 3, '2018-10-30 15:00:00'),
( 4, '2018-10-30 16:00:00'),
( 5, '2018-10-30 17:00:00'),
( 6, '2018-10-30 18:00:00'),
( 7, '2018-10-30 19:00:00'),
( 8, '2018-10-31 05:00:00'),
( 9, '2018-10-31 06:00:00'),
(10, '2018-10-31 07:00:00');
CREATE TABLE Tag (
id integer primary key,
dt varchar(20)
);
INSERT INTO Tag (id, dt) VALUES
(100, '2018-10-30 16:08:00'),
(101, '2018-10-30 17:30:00'),
(102, '2018-10-30 19:12:00'),
(103, '2018-10-31 04:00:00'),
(104, '2018-10-31 13:00:00');
The following query gives me the good match (based on diff) but I'm unable to get Tick columns:
SELECT Tag.dt,
(SELECT ABS(strftime('%s',Tick.dt) - strftime('%s',Tag.dt)) as diff
FROM Tick
ORDER BY diff ASC
LIMIT 1
) as diff from Tag
I tried the following but I receive an error on Tag.dt in ORDER BY:
SELECT
Tag.id, Tag.dt,
Tick.id, Tick.dt,
abs(strftime('%s',Tick.dt) - strftime('%s',Tag.dt)) as Diff FROM Tag JOIN Tick ON Tick.dt = (SELECT Tick.dt
FROM Tick
ORDER BY abs(strftime('%s',Tick.dt) - strftime('%s',Tag.dt)) ASC
limit 1)
The result I would like to have is something like:
TagID,DateTimeTag ,TickID,DateTimeTick
100,2018-10-30 16:08:00, 4,2018-10-30 16:00:00
101,2018-10-30 17:30:00, 6,2018-10-30 18:00:00
102,2018-10-30 19:12:00, 7,2018-10-30 19:00:00
103,2018-10-31 04:00:00, 8,2018-10-31 05:00:00
104,2018-10-31 13:00:00, 10,2018-10-31 07:00:00
Edited later...
Based on forpas's answer, I was able to derive something without using the ROW_COUNTER() keyword which I can't use in FME. I also set a maximum delta time difference (10000 sec) to find a match:
SELECT t.TagId, t.Tagdt, t.TickId, t.Tickdt, MIN(t.Diff)
FROM
(
SELECT
Tag.id as TagId, Tag.dt as Tagdt,
Tick.id as TickId, Tick.dt as Tickdt,
abs(strftime('%s',Tick.dt) - strftime('%s',Tag.dt)) as Diff
FROM Tag, Tick
WHERE Diff < 10000
) AS t
GROUP BY t.TagId
Thanks again!
Use ROW_NUMBER() window function:
SELECT t.tagID, t.tagDT, t.tickID, t.tickDT
FROM (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY t.tagID, t.tagDT ORDER BY t.Diff) AS rn
FROM (
SELECT Tag.id tagID, Tag.dt tagDT, Tick.id tickID, Tick.dt tickDT,
ABS(strftime('%s',Tick.dt) - strftime('%s',Tag.dt)) as Diff
FROM Tag CROSS JOIN Tick
) AS t
) AS t
WHERE t.rn = 1
See the demo.
Rsults:
| tagID | tagDT | tickID | tickDT |
| ----- | ------------------- | ------ | ------------------- |
| 100 | 2018-10-30 16:08:00 | 4 | 2018-10-30 16:00:00 |
| 101 | 2018-10-30 17:30:00 | 5 | 2018-10-30 17:00:00 |
| 102 | 2018-10-30 19:12:00 | 7 | 2018-10-30 19:00:00 |
| 103 | 2018-10-31 04:00:00 | 8 | 2018-10-31 05:00:00 |
| 104 | 2018-10-31 13:00:00 | 10 | 2018-10-31 07:00:00 |
Create a temp_table query to get the differences of time stamps of the cross product of Tick and Tag tables and select the min value for each of the Tick table id s.
The two temp_table queries are identical.
Note that this query may not be efficient as it takes full cross product across the two tables
SELECT temp_table.tid, temp_table.tdt, temp_table.tiid, temp_table.tidt, temp_table.diff
FROM
(SELECT Tag.id AS tid, Tag.dt AS tdt, Tick.id AS tiid, Tick.dt AS tidt, abs(strftime('%s',Tick.dt) - strftime('%s',Tag.dt)) as diff
FROM tag, tick) temp_table
WHERE temp_table.diff =
(SELECT MIN(temp_table2.diff) FROM
(SELECT Tag.id AS tid, Tag.dt AS tdt, Tick.id AS tiid, Tick.dt AS tidt, abs(strftime('%s',Tick.dt) - strftime('%s',Tag.dt)) as diff
FROM tag, tick) temp_table2
WHERE temp_table2.tid = temp_table.tid
)
group by temp_table.tid

Is there a way to reuse subqueries in the same query?

See Update at end of question for solution thanks to marked answer!
I'd like to treat a subquery as if it were an actual table that can be reused in the same query. Here's the setup SQL:
create table mydb.mytable
(
id integer not null,
fieldvalue varchar(100),
ts timestamp(6) not null
)
unique primary index (id, ts)
insert into mydb.mytable(0,'hello',current_timestamp - interval '1' minute);
insert into mydb.mytable(0,'hello',current_timestamp - interval '2' minute);
insert into mydb.mytable(0,'hello there',current_timestamp - interval '3' minute);
insert into mydb.mytable(0,'hello there, sir',current_timestamp - interval '4' minute);
insert into mydb.mytable(0,'hello there, sir',current_timestamp - interval '5' minute);
insert into mydb.mytable(0,'hello there, sir. how are you?',current_timestamp - interval '6' minute);
insert into mydb.mytable(1,'what up',current_timestamp - interval '1' minute);
insert into mydb.mytable(1,'what up',current_timestamp - interval '2' minute);
insert into mydb.mytable(1,'what up, mr man?',current_timestamp - interval '3' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '4' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '5' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '6' minute);
What I want to do is return only rows where FieldValue differs from the previous row. This SQL does just that:
locking row for access
select id, fieldvalue, ts from
(
--locking row for access
select
id, fieldvalue,
min(fieldvalue) over
(
partition by id
order by ts, fieldvalue rows
between 1 preceding and 1 preceding
) fieldvalue2,
ts
from mydb.mytable
) x
where
hashrow(fieldvalue) <> hashrow(fieldvalue2)
order by id, ts desc
It returns:
+----+---------------------------------+----------------------------+
| id | fieldvalue | ts |
+----+---------------------------------+----------------------------+
| 0 | hello | 2015-05-06 10:13:34.160000 |
| 0 | hello there | 2015-05-06 10:12:34.350000 |
| 0 | hello there, sir | 2015-05-06 10:10:34.750000 |
| 0 | hello there, sir. how are you? | 2015-05-06 10:09:34.970000 |
| 1 | what up | 2015-05-06 10:13:35.470000 |
| 1 | what up, mr man? | 2015-05-06 10:12:35.690000 |
| 1 | what up, duder? | 2015-05-06 10:09:36.240000 |
+----+---------------------------------+----------------------------+
The next step is to return only the last row per ID. If I were to use this SQL to write the previous SELECT to a table...
create table mydb.reusetest as (above sql) with data;
...I could then do this do get the last row per ID:
locking row for access
select t1.* from mydb.reusetest t1,
(
select id, max(ts) ts from mydb.reusetest
group by id
) t2
where
t2.id = t1.id and
t2.ts = t1.ts
order by t1.id
It would return this:
+----+------------+----------------------------+
| id | fieldvalue | ts |
+----+------------+----------------------------+
| 0 | hello | 2015-05-06 10:13:34.160000 |
| 1 | what up | 2015-05-06 10:13:35.470000 |
+----+------------+----------------------------+
If I could reuse the subquery in my initial SELECT, I could achieve the same results. I could copy/paste the entire query SQL into another subquery to create a derived table, but this would just mean I'd need to change the SQL in two places if I ever needed to modify it.
Update
Thanks to Kristján, I was able to implement the WITH clause into my SQL like this for perfect results:
locking row for access
with items (id, fieldvalue, ts) as
(
select id, fieldvalue, ts from
(
select
id, fieldvalue,
min(fieldvalue) over
(
partition by id
order by ts, fieldvalue
rows between 1 preceding and 1 preceding
) fieldvalue2,
ts
from mydb.mytable
) x
where
hashrow(fieldvalue) <> hashrow(fieldvalue2)
)
select t1.* from items t1,
(
select id, max(ts) ts from items
group by id
) t2
where
t2.id = t1.id and
t2.ts = t1.ts
order by t1.id
Does WITH help? That lets you define a result set you can use multiple times in the SELECT.
From their example:
WITH orderable_items (product_id, quantity) AS
( SELECT stocked.product_id, stocked.quantity
FROM stocked, product
WHERE stocked.product_id = product.product_id
AND product.on_hand > 5
)
SELECT product_id, quantity
FROM orderable_items
WHERE quantity < 10;

Oracle 11g: Replace part of string using dictionary mapping

Is there any nice trick to change values in string using dictionary mapping? For example I have table1 FIDDLE
+---------------------------+
| ROW1 |
+---------------------------+
| This is an example string |
| This String has typ0s |
+---------------------------+
And some mapping table dict1 FIDDLE:
+-------------------------+
| OLD | NEW |
+-------------------------+
| THIS | THAT |
| IS | ARE |
| EXAMPLE | SOURCE |
| STRING | NUMBER |
+------------+------------+
I need some SELECT statement that will split values in table1.row1 and change words using mapping dictionary dict1 so received values will be ( changing no existing dictionary values to upper is optional):
+---------------------------+
| TRANS_ROW1 |
+---------------------------+
| THAT ARE AN SOURCE NUMBER |
| THAT NUMBER HAS TYP0S |
+---------------------------+
PS. Spliting using REGEXP expression will be so nice..
WITH dict1 AS
(SELECT 'THIS' fr,
'THAT' t
FROM dual
UNION ALL
SELECT 'IS' fr,
'ARE' t
FROM dual
UNION ALL
SELECT 'EXAMPLE' fr,
'SOURCE' t
FROM dual
UNION ALL
SELECT 'STRING' fr,
'NUMBER' t
FROM dual),
table1 AS
(SELECT 'This is an example string' AS str,
1 AS sn
FROM dual
UNION ALL
SELECT 'This String has typ0s' AS str,
2 sn
FROM dual),
src AS
(SELECT regexp_substr(upper(s.str), '[^ ]+', 1, LEVEL) str2,
s.*,
rownum nn
FROM table1 s
CONNECT BY instr(TRIM(' ' FROM str), ' ', 1, LEVEL - 1) > 0
AND PRIOR sn = sn
AND PRIOR dbms_random.value IS NOT NULL),
repl AS
(SELECT nvl2(dict1.t, dict1.t, src.str2) lex,
sn,
nn
FROM src
LEFT JOIN dict1
ON dict1.fr = src.str2)
SELECT listagg(lex, ' ') within GROUP(ORDER BY nn),
sn
FROM repl
GROUP BY sn
It works now as you ask. Enjoy.
EDIT: FIDDLE with solution

Suggestion needed writing a complex query - sqlite

I have 4 columns in a table called musics - 'artist','genre', 'writer' , 'producer'.
I need to write a query such that, it returns a value 0 , if there are no repetition of values corresponding to the column name; if there is a repetition of values, it should return a value 1, corresponding to that column name.
Any help is much appreciated
SELECT (COUNT(artist) <> COUNT(DISTINCT artist)) artist,
(COUNT(genre) <> COUNT(DISTINCT genre)) genre,
(COUNT(writer) <> COUNT(DISTINCT writer)) writer,
(COUNT(producer) <> COUNT(DISTINCT producer)) producer
FROM musics
Another version
SELECT
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY artist HAVING COUNT(*) > 1) a
) artist,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY genre HAVING COUNT(*) > 1) g
) genre,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY writer HAVING COUNT(*) > 1) w
) writer,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY producer HAVING COUNT(*) > 1) p
) producer
Sample data
| artist | genre | writer | producer |
------------------------------------------
| artist1 | genre1 | writer1 | producer1 |
| artist2 | genre2 | writer1 | producer2 |
| artist1 | genre3 | writer3 | producer3 |
Sample output:
| artist | genre | writer | producer |
--------------------------------------
| 1 | 0 | 1 | 0 |
SQLFiddle
For Artist
select convert(bit,(count(1)-1))
from table_name
group by artist -- <-- Replace artist with column name for which duplicate
write a select count statement using distinct with specified column and another select count without distinct and compare both of them based on your requirement
you can use 4 different query with union & each query must contain count(column name) + group by clause

Resources