Alternative for "connect by" Oracle SQL - sql

Anyone be able to give me some script that does the same as the below but not using "connect by"? The code works well, but I cannot use connect by in the BI publisher.
select to_char(to_date('2022-05-01') + (level -1),'YYYY-MM-DD') as read_date
from dual
connect by to_date('2022-05-01') + (level -1) <= to_date('2022-05-05')

CONNECT BY is Oracle's propriatary and concise way to write a recursive query. You can replace it with a standard SQL compliant recursive query that has been supported by Oracle since 2002.
with read_dates(read_date) as
(
select date '2022-05-01' from dual
union all
select read_date + interval '1' day from read_dates
where read_date < date '2022-05-05'
)
select to_char(read_date, 'YYYY-MM-DD')
from read_dates;
Two remarks:
Your own code is vulnarable, because it uses an implicit string to date conversion (to_date('2022-05-01')) that relies on session date settings and can thus fail miserably.
It is rare that we select dates as strings (to_char(..., 'YYYY-MM-DD')), because we usually want our app to know that we are selecting dates.

Related

Dynamic query using bigquery and data studio

I want to take out data for every date range in Data Studio without the need to change date range selectors in my BigQuery all the time. However, not sure if it is even possible to do so. The reasons I do this is to make sure that the queried data is only for 30 days, as later it do some kind of segmentation using that 30 days data.
Then I figured out that the Data Studio can use dynamic_date, however this way will never produce any datatable (datatable will be used to do other queries from it). Is it possible to do dynamic_date in BigQuery instead? like retrieving data from BigQuery using a date range not previously defined in the query.
From my point of view, code should be like :
SELECT
ID,
FROM `table`
WHERE DATE(Timestamp) between $DS_START_DATE and $DS_START_DATE + INTERVAL 30 DAY)
or
WHERE DATE(Timestamp) >= #DS_START_DATE
I believe in pure Bigquery you can use DECLARE clause for that purpose, defining variables of the specified type:
declare DS_START_DATE date default "2020-03-03";
declare DS_END_DATE date default "2020-03-04";
WITH sample AS (
SELECT '10001' AS id, cast('2020-03-01' AS timestamp) as date_id UNION ALL
SELECT '10002', cast('2020-03-02' AS timestamp) UNION ALL
SELECT '10003', cast('2020-03-03' AS timestamp) UNION ALL
SELECT '10004', cast('2020-03-04' AS timestamp) UNION ALL
SELECT '10005', cast('2020-03-05' AS timestamp) UNION ALL
SELECT '10006', cast('2020-03-06' AS timestamp)
)
select id, date_id from sample
where date(date_id) between DS_START_DATE and DS_END_DATE
Alternatively, you can take a look at parameterized queries, however as I mentioned in the comment, they are not supported in classic BigQuery web UI.

Recursive Query for Date Range

I'm trying to create a query so that I can generate a date range between a specific start and end point.
I have the following:
WITH DATE_RANGE(DATE_FOR_SHIFT)
AS (SELECT DATE('2015-04-01')
FROM SYSIBM.SYSDUMMY1
UNION ALL
SELECT DATE_FOR_SHIFT + 1 DAY
FROM DATE_RANGE
WHERE DATE_FOR_SHIFT <= #END)
SELECT DATE_FOR_SHIFT
FROM DATE_RANGE;
Output (assuming that #END equals 2015-05-01):
2015-04-01
2015-04-02
2015-04-03
2015-04-04
...
2015-05-01
The output is correct, but I want to be able to change the start and points based on parameters provided rather than having to rewrite the query or have a SQL injection prone query.
How would I rewrite this query in order to accomplish this?
Your SELECT is fine, other than the hard coded start date.
What I think you're missing is wrapping it in either a stored procedure or user defined table function (UDTF). Assuming you'll want to JOIN the date range to other tables, I'd suggest a UDTF.
create function date_range (#str date, #end date)
returns table (date_for_shift date)
language SQL
reads SQL data
return
WITH DATE_RANGE(DATE_FOR_SHIFT)
AS (SELECT #str
FROM SYSIBM.SYSDUMMY1
UNION ALL
SELECT DATE_FOR_SHIFT + 1 DAY
FROM DATE_RANGE
WHERE DATE_FOR_SHIFT <= #END)
SELECT DATE_FOR_SHIFT
FROM DATE_RANGE;
Then you'd call it...
select * from table(date_range(date('2015-04-01'),date('2015-05-01'))) as tbl;
However, instead of generating this date range on the fly....consider simply creating a calender (aka dates) table. Basically just a table with dates from say 1900-01-01 to 2500-12-31..or whatever you'd like. Beside the date column, you can include lots of additional columns such as business_day, holiday, ect.. that make life much easier.
Google "SQL calendar table" for plenty of examples.
A bit of playing with this in perl gives me:
#!/opt/myperl/5.20.2/bin/perl
use 5.10.0;
use DBI;
use DBD::DB2;
use Data::Dump;
my $sql = <<EOSQL;
WITH DATE_RANGE(DATE_FOR_SHIFT)
AS (SELECT CAST(? AS DATE)
FROM SYSIBM.SYSDUMMY1
UNION ALL
SELECT DATE_FOR_SHIFT + 1 DAY
FROM DATE_RANGE
WHERE DATE_FOR_SHIFT < CAST(? AS DATE))
SELECT DATE_FOR_SHIFT
FROM DATE_RANGE;
EOSQL
my $dbh = DBI->connect('dbi:DB2:sample');
my $sth = $dbh->prepare_cached($sql);
$sth->execute('2015-04-01','2015-05-01');
my $rc = $sth->fetchall_arrayref();
dd($rc);
This does give an error during prepare ("The recursive common table expression "MYSCHEMA.DATE_RANGE" may contain an infinite loop") that I haven't figured out yet, but the fetch does work, the final return goes from 04-01 to 05-01. Hopefully you can port this to your desired language.

SYSDATE FROM DUAL in another SELECT

I have problems with time in my select. Time in DB is different than system time so I guess I need to use SELECT SYSDATE FROM DUAL but it doesn't work in another select. I use PL/SQL Developer.
SELECT t.something1,
ROUND((TRUNC(SELECT SYSDATE FROM DUAL) - 1 + 2),1) AS NAME
FROM customers t
WHERE t.something1 = ROUND((TRUNC(SELECT SYSDATE FROM DUAL) - 1 + 2),1);
I have also tried to declare new variable but I always get error message.
DECLARE
day DATE
SELECT SYSDATE
INTO day
FROM DUAL
SELECT t.something1,
ROUND((TRUNC(day)- 1 + 2),1) AS NAME
FROM customers t
WHERE t.something1 = ROUND((TRUNC(day) - 1 + 2),1);
You must use SELECT SYSDATE FROM DUAL to get SYSDATE only if you have no table to select from. This is because a SELECT without a FROM is not valid with Oracle, so when you want to use a SQL function and you have no table, you have to use DUAL, which is a special one-row one-column (named DUMMY) table. The only row has the value X.
ROUND(number) function will only work with number data type. It will not work with the date type.
If your t.something1 column data type is date than you can't used round() function (you can directly compare date with date), if it not and is number than you will use round() and you need to convert your sysdate into number and compare.
As per my understanding you do something like below :
SELECT t.something1,
ROUND(to_number(to_char(sysdate+1,'DDMMYYYY')),1) AS NAME
FROM customers t
WHERE t.something1 = ROUND(to_number(to_char(sysdate+1,'DDMMYYYY')),1);
May this will help you.
You don't need the subquery:
SELECT t.something1,
ROUND((TRUNC(SYSDATE) - 1 + 2),1) AS NAME
FROM customers t
WHERE t.something1 = ROUND((TRUNC(SYSDATE) - 1 + 2),1);
Does this fix your problem?

PLSQL 'IN' Operator

I'm trying to select rows based on a range of 'dates' determined by the following PLSQL query, which currently delivers the results I need - being the 'date' object of the last 10 weeks of the day of the week when the script is run. Eg. running it on the 22th of May would yield, 15th May, 8th May and so on.
SELECT SYSDATE-(level*7) as DateRange
FROM DUAL
CONNECT BY LEVEL <=10
This generates a list of dates. Then I try and combine this with a parent select statement to get rows with the dates outputted by the above that are in the 'DAY' (of Oracle type DATE) column.
SELECT * FROM NEM_RM16
WHERE NEM_RM16.DAY IN (
SELECT SYSDATE-(level*7) as DateRange
FROM DUAL
CONNECT BY LEVEL <=10);
Which gives no results, despite knowing that there are rows that have the dates generated by the above.
I've read that when using the 'IN' operator, values must be enclosed in single quotes, but I'm not sure about how to do this with the query above.
Am I going about this the right way by using the IN operator, or should I be doing a different type of nested query?
use trunc for truncate time component from sysdate
SELECT * FROM NEM_RM16
WHERE NEM_RM16.DAY IN (
SELECT trunc(SYSDATE)-(level*7) as DateRange
FROM DUAL
CONNECT BY LEVEL <=10);
Maybe the format of the date returned by nested query does not match with the date format of the column NEM_RM16.DAY
Probably, if the dates are compared after making them of the same format, they will match properly
Like this
SELECT *
FROM NEM_RM16
WHERE TO_DATE(TO_CHAR(NEM_RM16.DAY, 'DD/MM/YYYY'), 'DD/MM/YYYY') IN
(SELECT TO_DATE(TO_CHAR(SYSDATE - (level * 7), 'DD/MM/YYYY'),
'DD/MM/YYYY') as DateRange
FROM DUAL
CONNECT BY LEVEL <= 10);
Hope it helps

How to get one day ahead of a given date?

Suppose I have a date 2010-07-29. Now I would like to check the result of one day ahead. how to do that
For example,
SELECT *
from table
where date = date("2010-07-29")
How to do one day before without changing the string "2010-07-29"?
I searched and get some suggestion from web and I tried
SELECT *
from table
where date = (date("2010-07-29") - 1 Day)
but failed.
MySQL
SELECT *
FROM TABLE t
WHERE t.date BETWEEN DATE_SUB('2010-07-29', INTERVAL 1 DAY)
AND '2010-07-29'
Change DATE_SUB to DATE_ADD if you want to add a day (and reverse the BETWEEN parameters).
SQL Server
SELECT *
FROM TABLE t
WHERE t.date BETWEEN DATEADD(dd, -1, '2010-07-29')
AND '2010-07-29'
Oracle
SELECT *
FROM TABLE t
WHERE t.date BETWEEN TO_DATE('2010-07-29', 'YYYY-MM-DD') - 1
AND TO_DATE('2010-07-29', 'YYYY-MM-DD')
I used BETWEEN because the date column is likely DATETIME (on MySQL & SQL Server, vs DATE on Oracle), which includes the time portion so equals means the value has to equal exactly. These queries give you the span of a day.
If you're using Oracle, you can use the + and - operators to add a number of days to a date.
http://psoug.org/reference/date_func.html
Example:
SELECT SYSDATE + 1 FROM dual;
Will yield tomorrow's date.
If you're not using Oracle, please tell use what you ARE using so we can give better answers. This sort of thing depends on the database you are using. It will NOT be the same across different databases.
Depends of the DateTime Functions available on the RDBMS
For Mysql you can try:
mysql> SELECT DATE_ADD('1997-12-31',
-> INTERVAL 1 DAY);
mysql> SELECT DATE_SUB('1998-01-02', INTERVAL 31 DAY);
-> '1997-12-02'
If youre using MSSQL, you're looking for DateAdd() I'm a little fuzzy on the syntax, but its something like:
Select * //not really, call out your columns
From [table]
Where date = DateAdd(dd, -1, "2010-07-29",)
Edit: This syntax should be correct: it has been updated in response to a comment.
I may have the specific parameters in the wrong order, but that should get you there.
In PL SQL : select sysdate+1 from dual;