I have a query that has three prompts; Department, From Date, and To Date. One must select the department ID but has a an option to select the date range. How can I make the date range optional? I was thinking of using the decode function but not sure how to write it so the two date prompts can be left blank.
If you are using a stored procedure you can do something like this in your select statement:
select *
from table
where (field > inDateStart and field < inDateEnd) or
(inDateStart is null and inDateEnd is null)
or using coalesce
select *
from table
where (field => coalesce(inDateStart,field) and
field <= coalesce(inDateEnd,field)
It really depends on your particular situation. Some queries lend themselves to the first some to the second.
Assuming an unspecified date input comes across as NULL, you can do this little trick:
with
TheTable as
(select 1 dept, sysdate dt from dual
union
select 2 dept, sysdate-63 dt from dual
union
select 3 dept, sysdate-95 dt from dual
)
select *
from thetable
where coalesce(:DateFrom,dt) <= dt
and coalesce(:DateTo,dt) >= dt
;
Need a bit more info on the nature of your data to consider dept as an input... Does the table store multiple dates per dept?
Related
I am unioning two queries but I'm getting an ID that occurs in each query. I do not know how to keep only the first time the id occurs. Everything else about the row is different. In general, it will be hard to know which of the two queries I will have to keep a duplicate on, therefore, I need a general solution.
I was thinking about creating a temp table and choosing the min date (once the date has been converted to an int).
Any ideas on the proper syntax?
You can do this using the row_number() function. This will assign a sequential number, starting with 1, to each row with the same id (based on the partition by clause). The ordering of the sequence is determined by the order by clause. So, the following assigns 1 to the earliest date for each id:
select t.*
from (select t.*,
row_number() over (partition by id order by date asc) as seqnum
from ((select *
from <subquery1>
) union all
(select *
from <subquery2>
)
) t
) t
where seqnum = 1;
The final where clause simply filters for the first occurrence.
If you use the keyword UNION, then it will remove duplicates from the two data sets you are working with. UNION ALL preserves duplicates.
You can view the specifics here:
http://www.w3schools.com/sql/sql_union.asp
If you want to only have one of the 2 records and they are not identical you will have to filter them yourself. You may need to do something like the following. THis may be possible to do with the one (select union select) block but this should get you started.
select *
from (
select id
, date
, otherstuf
from table_1
union all
select id
, date
, otherstuf
from table_2
) x1
, (
select id
, date
, otherstuf
from table_1
union all
select id
, date
, otherstuf
from table_2
) x2
where x1.id = x2.id
and x1.date < x2.date
Although rethinking this if you go down a path like this why bother to UNION it?
I am working on a historical conversion of data and was wondering if there's a more efficient way to accomplish a date increment.
I receive a data from a source system on a saturday date (1-7-13) and would like to push that data to make it fill all days of the previous week (1-6-13,1-5-13 ect).
So currently i am doing several unions
insert into target
(date, name)
select date,name
from
(
SELECT date as date, name FROM SOURCE
UNION
SELECT date - 1 as date, name FROM SOURCE
UNION
SELECT date -2 as date, name FROM SOURCE
)
I only ask because it looks like close to 500 million records are going to be going though this sql script. Incase it matters it is going to be running in a BTEQ script in TERADATA.
First, your code would be faster using union all rather than union. union removes duplicates, which does not seem to be needed in this case. If you do need them removed, then do it at the source level:
from (select distinct name from source)
Rather than doing it implicitly with union.
You can also try a cross join approach:
select date - i, name
from source cross join
(select 0 as i union all select 1 union all select 2 union all select 3 union all
select 4 union all select 5 union all select 6
) const
This might be a bit faster, because it doesn't need to set up the reads to the table multiple times.
One option is to use a recursive query, but I don't think it would be much faster -- just perhaps easier to read:
WITH RECURSIVE recursiveCTE (date, name) AS (
SELECT date, name
FROM Source
UNION ALL
SELECT r.date-1, r.name
FROM recursiveCTE R
JOIN Source T ON R.name = T.name AND T.date < r.date+6
)
INSERT INTO Target (date,name)
SELECT date,name From recursiveCTE
There is a column (REC_CREATE_TIMESTAMP) in a table which shows date in format YY-MM-DD. I would like select the latest (by time) of a certain date (let's say Aug 31, 2011). The first column should be the timestamp in hh24:mm:ss and the rest should be all columns (*) of this table. How do I do it?
Using Oracle 2.1.1.64
REC_CREATE_TIMESTAMP - NOT NULL - DATE
SELECT * FROM Table WHERE REC_CREATE_TIMESTAMP IN
(
SELECT
MAX(REC_CREATE_TIMESTAMP) as REC_CREATE_TIMESTAMP
FROM
Table
WHERE
TO_CHAR(REC_CREATE_TIMESTAMP, 'YYYYMMDD') = '20110831'
)
EDITED for Oracle
SELECT *
FROM tableName As tbl1
WHERE EXISTS
(
SELECT NULL
FROM tableName as tbl2
WHERE
tbl2.rec_create_timestamp > '2001-08-30'
tbl2.rec_create_timestamp < '2001-09-01'
HAVING MAX(tbl2.rec_create_timestamp) = tbl1.rec_create_timestamp
)
For a generalised answer that can work with Sets of data rather than individual dates...
WITH
sequenced_data AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY TRUNC(rec_create_timestamp) ORDER BY rec_create_timestamp DESC) AS daily_sequence_id,
TRUNC(rec_create_timestamp) as rec_create_date,
*
FROM
yourData
)
SELECT
*
FROM
sequenced_data
WHERE
daily_sequence_id = 1
AND rec_create_date = '20110831'
I have a really big query in which makes some troubles for me because one join can return several rows. I only want the latest row (identified by a date field) in this result set, but I cant seem to put together the correct query to make it work.
The query I need MAX date from is:
SELECT custid,reason,date FROM OPT opt WHERE opt.custid = 167043;
Teh custid is really found through a join, but for simplicity I've added it to the where clause here. This query produces the following result:
custid grunn date
167043 "Test 1" 19.10.2005 12:33:18
167043 "Test 2" 28.11.2005 16:23:35
167043 "Test 3" 14.06.2010 15:43:16
How can I retrieve only one record from this resultset? And that record is the one with the highest date? Ultimately Im putting this into a big query which does alot of joins, so hopefully I can use this example into my bigger query.
You can do this:
SELECT * FROM
( SELECT custid,reason,date FROM OPT opt WHERE opt.custid = 167043
ORDER BY date DESC
)
WHERE ROWNUM = 1;
You can solve it by using analytic functions. Try something like this:
select custid
,reason
,date
from (select custid
,reason
,date
,row_number() over(partition by cust_id order by date desc) as rn
from opt)
where rn = 1;
This is how it works: The resultset is divided into groups of cust_id (partition by). In each group, the rows will be sorted by the date column in descending order (order by). Each row within the group will be assigned a sequence number (row_number) from 1 to N.
This way the row with the highest value for date will be assigned 1, the second latest 2, third latest 3 etc..
Finally, I just pick the rows with nr = 1, which basically filters out the other rows.
Or another way using the LAST function in its aggregate form.
with my_source_data as (
select 167043 as custid, 'Test 1' as reason, date '2010-10-01' as the_date from dual union all
select 167043 as custid, 'Test 2' as reason, date '2010-10-02' as the_date from dual union all
select 167043 as custid, 'Test 3' as reason, date '2010-10-03' as the_date from dual union all
select 167044 as custid, 'Test 1' as reason, date '2010-10-01' as the_date from dual
)
select
custid,
max(reason) keep (dense_rank last order by the_date) as reason,
max(the_date)
from my_source_data
group by custid
I find this quite useful as it rolls the process of finding the last row and the value all into one. The use of MAX (or another aggregate function such as MIN) in case that the combination of the grouping and the order by is not deterministic.
This function will basically take the contents of the column based on the grouping, order it by the ordering given then take the last value.
rather than using row_number() I think it's better to select what you actually want to select (e.g. the last date)
SELECT custid
, reason
, date
from
(
SELECT custid
, reason
, date
, max(opt.date) over (partition by opt.custid order by opt.date) last_date
FROM OPT opt
WHERE opt.custid = 167043;
)
where date = last_date
both solutions with ROW_NUMBER and KEEP are good. I would tend to prefer ROW_NUMBER when retrieving a large number of columns, and keep KEEP for one or two columns, otherwise you will have to deal with duplicates and the statement will get pretty unreadable.
For a small number of columns however, KEEP should perform better
If I have a table with columns id, name, score, date
and I wanted to run a sql query to get the record where id = 2 with the earliest date in the data set.
Can you do this within the query or do you need to loop after the fact?
I want to get all of the fields of that record..
If you just want the date:
SELECT MIN(date) as EarliestDate
FROM YourTable
WHERE id = 2
If you want all of the information:
SELECT TOP 1 id, name, score, date
FROM YourTable
WHERE id = 2
ORDER BY Date
Prevent loops when you can. Loops often lead to cursors, and cursors are almost never necessary and very often really inefficient.
SELECT TOP 1 ID, Name, Score, [Date]
FROM myTable
WHERE ID = 2
Order BY [Date]
While using TOP or a sub-query both work, I would break the problem into steps:
Find target record
SELECT MIN( date ) AS date, id
FROM myTable
WHERE id = 2
GROUP BY id
Join to get other fields
SELECT mt.id, mt.name, mt.score, mt.date
FROM myTable mt
INNER JOIN
(
SELECT MIN( date ) AS date, id
FROM myTable
WHERE id = 2
GROUP BY id
) x ON x.date = mt.date AND x.id = mt.id
While this solution, using derived tables, is longer, it is:
Easier to test
Self documenting
Extendable
It is easier to test as parts of the query can be run standalone.
It is self documenting as the query directly reflects the requirement
ie the derived table lists the row where id = 2 with the earliest date.
It is extendable as if another condition is required, this can be easily added to the derived table.
Try
select * from dataset
where id = 2
order by date limit 1
Been a while since I did sql, so this might need some tweaking.
Using "limit" and "top" will not work with all SQL servers (for example with Oracle).
You can try a more complex query in pure sql:
select mt1.id, mt1."name", mt1.score, mt1."date" from mytable mt1
where mt1.id=2
and mt1."date"= (select min(mt2."date") from mytable mt2 where mt2.id=2)