Dynamic start date from specific column in a table (sysdate) - sql

I am pretty new in this field, trying to learn slowly so please be patient with me :)
My database contains a table called t_usage_interval. In this table there is a column name ID_Interval. Each month a new random 10 digit number is created in this column.
This is the query I am using
I would like to find out if there is a way to pull the latest interval by using column name DT_START with SYSDATE option? I guess it would be a dynamic query search from a sysdate to display the latest ID_Interval?
Thank you,
A

This is how I understood the question.
A straightforward query returns row(s) whose dt_start is the first in that table that is lower or equal to sysdate (you might also use trunc(sysdate), if you don't care about time component). Drawback of this query is that it scans t_usage_Interval table twice.
select *
from t_usage_interval a
where a.dt_start = (select max(b.dt_start)
from t_usage_interval b
where b.dt_start <= sysdate
);
Somewhat less intuitive option is to "rank" rows (whose dt_start is lower than sysdate) by dt_start, and then return row(s) that rank the "highest". This option scans the table only once, so it should perform better.
with temp as
(select a.*,
rank() over (order by a.dt_start desc) rn
from t_usage_interval a
where a.dt_start <= sysdate
)
select t.*
from temp t
where t.rn = 1;

Related

SQL Server, get most recent record within a specified date range

I'm trying to figure out how to do this via a view if possible (definitely aware this can be done in-line, via a function, and/or a proc.
There's a view that needs to dedupe a dataset and pick the most recent record. So I'm trying to use wither row_number(), or even a top 1 order by via a cross apply, but the problem is that the query can filter on a date, so for eg.
select x
from view
where date < somedate
and the most recent record needs to be calculated for that filtered dataset. Is there a way to do this in a view? a correlated subquery comes to mind but try as I may, either I get the full duped dataset, or it picks the most recent on the table without a date filter, then applies the date filter after the fact, which isn't the same thing.
Some background per Yogesh:
The table in question contains history of an employee table, where each employee_id can exist multiple times with different date values. There is a primary key on this table which is an employeehistory_id (identity). The goal is to get the most recent record for all employees (1 unique record per employee) where the date < some date. The problem with the windowing is that it needs to almost have the date filter in a subquery within the view (from what I'm seeing). Hopefully that helps clarify the answer.
Currently the view would be something like
SELECT a.*
FROM employeehistory a
join (select employee_id, employeehistory_ID, row_number()OVER(PARTITION
BY employee_id ORDER BY Date DESC) as Ranked
FROM employeehistory) b
on a.employee_id = b.employee_id
and a.employeehistory_ID = b.employeehistory_ID
where b.Ranked = 1
so as you can see, filtering the view with a date, doesn't propagate necessarily to the inner. So asking to see if there is a way to still keep this functional in a view. Once again, I'm aware this can be done either as a function or a proc. Thanks!
We're using SQL Server 2016 Enterprise edition.
how about
select top 1 x
from view
where date < somedate
order by date desc
SELECT TOP 1 x
FROM [View]
WHERE
date < somedate
ORDER BY
date DESC
select *
from MyView
where myDate < somedate
order by myDate desc
limit 1;
limit 1 is the same as top 1 but for MySQL. I have a fiddle that shows what I think your expected output should look like for it as well.
http://sqlfiddle.com/#!9/209226/7

Getting peak value of a column in table till this date

I have an oracle table having columns {date, id, profit, max_profit}.
I have data in date and profit, and I want highest value of profit till date in max_profit, I am using query below
UPDATE MY_TABLE a SET a.MAX_PROFIT = (SELECT MAX(b.PROFIT)
FROM MY_TABLE b WHERE b.DATE <= a.DATE
AND a.id = b.id)
This is giving me correct result, but I have millions of rows for which query is taking considerable time, any faster way of doing it ?
You can use a MERGE statement with an analytic function:
MERGE INTO my_table dst
USING (
SELECT ROWID rid,
MAX( profit ) OVER ( PARTITION BY id ORDER BY "DATE" ) AS max_profit
FROM my_table
) src
ON ( src.rid = dst.ROWID )
WHEN MATCHED THEN
UPDATE SET max_profit = src.max_profit;
When you do something like "SELECT MAX(...)" you're going to scan all the records implicated in the 'WHERE" part of the query, so you want to make getting all those records as easy on the database as possible.
Do you have an index on the table that includes the id and date columns?
Depending on the behavior of this application, if you're doing a lot fewer updates/inserts (as opposed to doing a ton of reads during reporting or some other process), a possible performance enhancement might be to keep the value you're storing in the max_profit column up to date somewhere while you're changing the data. Have you considered a separate table that just stores the profit calculation for each possible date?

PostgreSQL limiting results by year

i have a working PostgreSQL query, column "code" is common in both tables and table test.a has date column and i want to limit search results on year, date format is like ( 2010-08-25 )
SELECT *
FROM test.a
WHERE form IN ('xyz')
AND code IN (
SELECT code
FROM test.city)
any help is appreciated
To return rows with date_col values in the year 2010:
SELECT *
FROM test.a
WHERE form = 'xyz'
AND EXISTS (
SELECT 1
FROM test.city
WHERE code = a.code
)
AND date_col >= '2010-01-01'
AND date_col < '2011-01-01';
This way, the query can use an index on date_col (or, ideally on (form, date_col) or (form, code, date_col) for this particular query). And the filter works correctly for data type date and timestamp alike (you did not disclose data types, the "date format" is irrelevant).
If performance is of any concern, do not use an expression like EXTRACT(YEAR FROM dateColumn) = 2010. While that seems clean and simple to the human eye it kills performance in a relational DB. The left-hand expression has to be evaluated for every row of the table before the filter can be tested. What's more, simple indexes cannot be used. (Only an expression index on (EXTRACT(YEAR FROM dateColumn)) would qualify.) Not important for small tables, crucial for big tables.
EXISTS can be faster than IN, except for simple cases where the query plan ends up being the same. The opposite NOT IN can be a trap if NULL values are involved, though:
Select rows which are not present in other table
If by "limit" you mean "filter", then I can give you an option
SELECT
*
FROM
test_a
WHERE
form IN ('xyz')
AND code IN (
SELECT code
FROM test_city
)
AND EXTRACT(YEAR FROM dateColumn) = 2010;
db-fiddle for you to run and play with it: https://www.db-fiddle.com/f/5ELU6xinJrXiQJ6u6VH5/6

Compare columns from two Oracle databases, matching field is date, one has time one doesn't

I need to compare and find a percent error for several columns from two different Oracle databases. DB1 DATE_TIME column only has date, no time (DD-MON-YY), DB2 DATE_TIME column has date and time. Each row represents one hour, and DB1 is in the correct order by hour, but with no actual times. Need the relevant columns to match up between id and date_time (specifically, hour), but I've found that a WHERE clause testing for DATE equality will only give the DB1 entry corresponding to 12:00:00 AM because of not having times as part of the date format in DB1, so I'm not able to to compare the correct entries for other times. How can I get around this?
Code below to better illustrate:
SELECT db1.field1, db2.field1, db1.date_time, db2.date_time
FROM db1, db2
WHERE db1.date_time = db2.date_time AND db1.id = X
ORDER BY db2.date_time DESC;
This query runs, but none of the data actually matches because it's only returning the first row of each day from DB1 (corresponding to 12:00:00 AM).
I've thought of somehow inserting corresponding time stamps to DB1 DATE_TIME column based off position so I can include time in the WHERE, but not sure how to do that, or if it will even work. I've seen that running a test query using BETWEEN day1 and day2 (instead of =) returns the results I want for a given range of days, but I'm not sure how to implement that in the JOIN that I'm trying to do with DB2.
Any ideas?
If you care about performance, I would suggest that you create a function-based index on db2:
create index idx_db2_datetime_date on db2(trunc(datetime));
Then, you can use this construct in the query:
SELECT db1.field1, db2.field1, db1.date_time, db2.date_time
FROM db1 JOIN
db2
ON db1.date_time = trunc(db2.date_time)
WHERE db1.id = X
ORDER BY db2.date_time DESC;
For this query, an index on db1(id, date_time) is also helpful.
The indexes are not necessary for the query to work, but the function-based index is a nice way to write a performant query with a function in the ON clause.
Note: Learn to use proper, explicit JOIN syntax. Never use commas in the FROM clause.
For a SELECT, you might want to try along:
SELECT db1_.field1, db2.field1, db1_.date_time, db2.date_time
FROM (
SELECT
id
, field1
, date_time + (RANK() OVER (PARTITION BY date_time ORDER BY id - 1) / 24 date_time
FROM DB1
) DB1_
JOIN db2
ON db1.date_time = TRUNC(db2.date_time, 'HH24')
AND db1.id = X
ORDER BY db2.date_time DESC;
If you prefer to get the hours added to DB1.date_time, please, try:
UPDATE
DB1 TDB1
SET TDB1.date_time =
(SELECT
d_t
FROM
(SELECT
id
, (date_time + (RANK() OVER (PARTITION BY date_time ORDER BY id) - 1) / 24) d_t
FROM DB1)
WHERE id = TD1.id
) DB1
;
Sorry, no suitable test data to verify in full at this time.
Please comment if and as this requires adjustment / further detail.

Oracle: SELECT where date is less if not equals null

I have a table of records, and one column holds the value when the records turns in-active.
Most of the records are still open, and therefore do not hold any value in the end_date column.
I want to select all of those records, which are still active. One way to achieve this (from the top of my head):
select *
from table t
where nvl(t.end_date, to_date('2099-DEC-31', 'MM-DD-yyyy')) > sysdate
But it doesn't feel right. Is there a better way to achieve what I want?
EDIT: BTW, the table isn't huge, and isn't going to grow :)
select *
from table t
where nvl(t.end_date, to_date('2099-DEC-31', 'MM-DD-yyyy')) > sysdate
won't use a "normal", non function based index, so it may hurt performance.
You could query it like
select *
from table t
where t.end_date > sysdate OR t.end_date is null
instead