Using the result of a query to determine a value in a where clause in SQL? - sql

So basically I've got a query where I want to filter using a date, however that date may change depending on what datas in the system.
If there's no records with a date of >='X', I want to use >='Y' instead
Currently I've got something like the following mess (Pseudocoded down to avoid using actual table names and such)
With a as (SELECT
count(column_id) as num
FROM tableA
WHERE ADate >= getdate() - 8)
,
b as (SELECT
case when num = '0' then getdate() - 15 else getdate() - 8 end as DateToUseInQuery
from A)
SELECT *
FROM tableB
Bunch of joins to other tables
WHERE BDate >= DateToUseInQuery
The general idea being is if there's no records for the week beforehand, use 2 weeks beforehand
I tried using a query within the where clause like:
WHERE BDate >= (SELECT DateToUseInQuery FROM b)
But the query ran for 11 minutes before I stopped it (Up from about 18 seconds before I tried to put this extra bit in)
I've been thinking about trying to set a variable as the date, but I can't do it in a CTE, and when I do it after, it breaks everything else.
So basically:
Is there an easier way to do this than the cack-handed way I'm trying?
If my way is fine, how can I pass that date properly into the WHERE clause?

You could try something like this. I am using a nested select and case statement to determine the number of days.
There may be a prettier way, but this works.
SELECT COUNT(column_id) AS num FROM TableA
WHERE ADate >=
DATEADD(Day,
(SELECT
CASE
WHEN EXISTS(
SELECT 1 FROM TableA
WHERE ADate >= DATEADD(Day,-8,GETDATE()))
THEN -8
ELSE -15
END AS 'dt')
,GETDATE());

Related

SQL Why am I getting the invalid identifier error?

I am trying to use columns that I created in this query to create another column.
Let me first my messy query. The query looks like this:
SELECT tb.team, tb.player, tb.type, tb.date, ToChar(Current Date-1, 'DD-MON-YY') as yesterday,
CASE WHEN to_date(tb.date) = yesterday then 1 else 0 end dateindicator,
FROM (
COUNT DISTINCT(*)
FROM TABLE_A, dual
where dateindicator = 1
Group by tb.team
)
What I am trying to do here is:
creating a column with "Yesterday's date"
Using the "Yesterday" column to create another column called dateindicator indicating each row is yesterday's data or not.
then using that dateindicator, I want to count the distinct number of player for each team that has 1 of the dateindicator column.
But I am getting the "invalid identifier" error. I am new to this oracle SQL, and trying to learn here.
You cannot use an Alias in your Select statement.
see here: SQL: Alias Column Name for Use in CASE Statement
you need to use the full toChar(.. in the CASE WHEN.
Also:
Your WHERE-condition (Line 5) doesnt belong there.. it should be:
SELECT DISTINCT .>. FROM .>. WHERE. you have to specify the table first. then you can filter it with where.
If I follow your explanation correctly: for each team, you want to count the number of players whose date column is yesterday.
If so, you can just filter and aggregate:
select team, count(*) as cnt
from mytable
where mydate >= trunc(sysdate) - 1 and mydate < trunc(sysdate)
group by team
This assumes that the dates are stored in column mydate, that is of date datatype.
I am unsure what you mean by counting distinct players; presumably, a given player appears just once per team, so I used count(*). If you really need to, you can change that to count(distinct player).
Finally: if you want to allow teams where no player matches, you can move the filtering logic within the aggregate function:
select team,
sum(case when mydate >= trunc(sysdate) - 1 and mydate < trunc(sysdate) then 1 else 0 end) as cnt
from mytable
group by team

tdate issue I'm facing in SQL query

While fetching count from table by using following query
Select count(*)
from tab
where tdate = '17-05-19' ---> output 0
or
Select count(*)
from tab
where trunc(tdate) = '17-05-19' ---->output 0
If I use:
Select count(*)
from tab
where tdate >sysdate - 1 ---> it returns some count(yesterday+some of the today txn)
But here I want only yesterday txn whenever I fire this query.
But here I want only yesterday txn whenever I fire this query.
You may use this.
Select count (*) from tab where
tdate >= TRUNC(SYSDATE) - 1
AND tdate < TRUNC(SYSDATE)
The advantage of this over using TRUNC on the date column is that it will utilize an index if it exists over tdate
If you tried by using
Select count(*) from tab where trunc(tdate) = date'2019-05-17'
(or, you could use
Select count(*) from tab where to_char(tdate,'dd-mm-yy') = '17-05-19' by formatting through to_char function
or, you could use
Select count(*) from tab where trunc(tdate) = trunc(sysdate)-1 to get only the data for the day before
)
you'd get some results provided you have data for the date 17th May.
So, you need to provide a formatting for your literal as date'2019-05-17'(known as date literal) especially for Oracle DB, it might be used as '2019-05-17' without date part in MySQL as an example.
Btw, trunc function is used to extract the date portion, and remove the time part of a date type column value.
If your table is populated with huge data, therefore performance may matter, then you can even create functional index on trunc(tdate).
Demo

Joining multiple tables returning duplicates

I am trying the following select statement including columns from 4 tables. But the results return each row 4 times, im sure this is because i have multiple left joins but i have tried other joins and cannot get the desired result.
select table1.empid,table2.name,table2.datefrom, table2.UserDefNumber1, table3.UserDefNumber1, table4.UserDefChar6
from table1
inner join table2
on table2.empid=table1.empid
inner join table3
on table3.empid=table1.empid
inner join table4
on table4.empid=table1.empid
where MONTH(table2.datefrom) = Month (Getdate())
I need this to return the data without any duplicates so only 1 row for each entry.
I would also like the "where Month" clause at the end look at the previous month not the current month but struggling with that also.
I am a bit new to this so i hope it makes sense.
Thanks
If the duplicate rows are identical on each column you can use the DISTINCT keyword to eliminate those duplicates.
But I think you should reconsider your JOIN or WHERE clause, because there has to be a reason for those duplicates:
The WHERE clause hits several rows in table2 having the same month on a single empid
There are several rows with the same empid in one of the other tables
both of the above is true
You may want to rule those duplicate rows out by conditions in WHERE/JOIN instead of the DISTINCT keyword as there may be unexpected behaviour when some data is changing in a single row of the original resultset. Then you start having duplicate empids again.
You can check if a date is in the previous month by following clause:
date BETWEEN dateadd(mm, -1, datefromparts(year(getdate()), month(getdate()), 1))
AND datefromparts(year(getdate()), month(getdate()), 1)
This statment uses DATEFROMPARTS to create the beginning of the current month twice, subtract a month from the first one by using DATEADD (results in the beginning of the previous month) and checks if date is between those dates using BETWEEN.
If your query is returning duplicates, then one or more of the tables have duplicate empid values. This is a data problem. You can find them with queries like this:
select empid, count(*)
from table1
group by empid
having count(*) > 1;
You should really fix the data and query so it returns what you want. You can do a bandage solution with select distinct, but I would not usually recommend that. Something is causing the duplicates, and if you do not understand why, then the query may not be returning the results you expect.
As for your where clause. Given your logic, the proper way to express this would include the year:
where year(table2.datefrom) = year(getdate()) and
month(table2.datefrom) = month(Getdate())
Although there are other ways to express this logic that are more compatible with indexes, you can continue down this course with:
where year(table2.datefrom) * 12 + month(table2.datefrom) = year(getdate()) * 12 + Month(Getdate()) - 1
That is, convert the months to a number of months since time zero and then use month arithmetic.
If you care about indexes, then your current where clause would look like:
where table2.datefrom >= dateadd(day,
- (day(getdate) - 1),
cast(getdate() as date) and
table2.datefrom < dateadd(day,
- (dateadd(month, 1, getdate()) - 1),
cast(dateadd(month, 1, getdate()) as date)
Eliminate duplicates from your query by including the distinct keyword immediately after select
Comparing against a previous month is slightly more complicated. It depends what you mean:
If the report was run on the 23rd Jan 2015, would you want 01/12/2014-31/12/2014 or 23/12/2014-22/01/2015?

Oracle sql count

I have below information in table and want to retrive the count if difference between two dates is >= 1.
Id testdate exdate
1 20120502 20120501 --> This should included, because diff is 1
2 20120601 20120601 --> This should not included, because diff is 0
3 20120704 20120703 --> This should included, because diff is 1
4 20120803 20120802 --> This should included, because diff is 1
Based on the above data, my select count should return 3.
I am trying the following, but it's not giving any results:
select count(to_char(testdate,'YYYYMMDD')-to_char(exdate,'YYYYMMDD')) from test ;
select count(*)
from my_table
where testdate <> exdate
You really should convert those to a date data-type though... it saves a lot of problems in the long run.
Your query will give you results. It will return 4. It gives you results because as long as the result of testdate - exdate is not null it will return a value for that row.
However, as you're not using dates Oracle will most probably convert those to numbers, which won't help for date comparisons should you do that in the future.
20120901 - 20120831 = 70 -- not 1
Okay, from your comment:
Working with ,if i use down voteaccept select count(*) from test where
to_char((testdate,'YYYYMMDD') - to_char(exdate,'YYYYMMDD')) >= 1; .But
count is one of the column.how to retrive above select statement as
one of the column
you're trying something completely different.
Your dates are actually dates; it's helpful to post this. You're looking for an analytic function, specifically count().
select a.*, count(*) over ( partition by 1 ) as ct
from my_table a
where trunc(exdate) <> trunc(testdate)
Note the trunc function, which, without additional parameters will remove the time portion of the date this enabling a direct comparison without resorting to converting the date to a character.
select count(*)
from test
where to_date(testdate,'YYYYMMDD') - to_date(exdate,'YYYYMMDD') >= 1;
or
select count(*)
from test
where to_date(testdate,'YYYYMMDD') <> to_date(exdate,'YYYYMMDD');
Looking at testdate and exdate it looks more like the columns are VARCHAR type so you would require apropriate date conversion.
In Oracle if the type is date you can calculate with them. 1 equal 1 day. 1/24 equals 1 hour.
Your case is rather easy because you could even compare the strings.
SELECT count(*)
FROM test
WHERE testdate <> exdate
But it sounds like you want to be able to be variable, so you rather convert them to a date and then you can do
SELECT count(*)
FROM test
WHERE to_date(testdate,'YYYYMMDD')-to_date(exdate,'YYYYMMDD') >= 1
I am not sure what you want if testdate minus exdate is -1 or more because the exdate is after testdate. Then you can work with ABS
SELECT count(*)
FROM test
WHERE ABS(to_date(testdate,'YYYYMMDD')-to_date(exdate,'YYYYMMDD')) >= 1

Get active rows between from and to date range inclusive of boundary

Table testTable has 4 columns
sessionid int
started datetime
ended datetime
SessionIsRunning bool - will be true if last session has not yet ended. Max only one record can be true at anytime since at the most only one session can be running. If no session is running then all records have this as false.
Given two dates say fromDate and toDate, how do I get the first session that started on or after fromDate and the last session that ended on or before toDate. Tricky condition is that if a session is in progress and it's start date >= fromDate we need this. I am guessing it might not be possible to get both the min and max session id in one sql statement and keep the code readable and easy to maintain. Two separate sql statements is ok. one to get min and one to get max. Then I can query rows using between min and max.
This last statement explains it in a different way. Get all sessions that started or was running and ended or was running between from/to dates thank you
After considering your edits and the comments regarding BETWEEN, this should give you the result set you need. It will pull any record where SessionIsRunning = true as well as sessions thats started AND ended in the date range.
SELECT * FROM testTable tt
WHERE (tt.started >= fromDate AND tt.started < DATEADD(DAY, 1, toDate)
AND tt.ended >= fromDate AND tt.ended < DATEADD(DAY, 1, toDate))
OR SessionIsRunning = true
ORDER BY tt.sessionid
Getting only the first and last value in a single query (because your question made me curious how to actually write this, even though that doesn't appear to be what you're really asking):
SELECT * FROM
(SELECT TOP 1 * FROM ATable ORDER BY AColumn ASC) first
UNION
SELECT * FROM
(SELECT TOP 1 * FROM ATable ORDER BY AColumn DESC) last