Flag on condition - sql

Here's my table :
key date
a 2002
a 2014
a 2011
b 2004
b 2016
b 2001
I'd like a SELECT statement that adds a flag for the most recent date, like that :
key date flag
a 2002 0
a 2014 1
a 2011 0
b 2004 0
b 2016 1
b 2001 0
Thanks

You can use an analytical function if you don't want to do a group by or self-join. You can probably consolidate this a little if you want to, but I find splitting it out using with makes it more obvious what is going on.
with max_date_query as (
select key, date, max(date) over (partition by key) max_date
from mytable
)
select key, date, case when date = max_date then 1 else 0 end flag
from max_date_query
There are other variations on the same theme where you can order the window by date desc and use row_number() instead of max() to determine the flag. I would imagine the one I showed is better, but not sure how much it will really make a difference. You might need to use that method if you have cases where you have duplicate max dates and need to really only choose one.

select t1.*, case when t2.a is null
then 0
else 1
end as flag
from your_table t1
left join
(
select key, max(date) as mdate
from your_table
group by key
) t2 on t1.key = t2.key and t1.date = t2.mdate

Not really sure what the "most recent" condition is (last "X" years?) and assuming the "2015" are in fact DATE values (not char), try:
select
t1.key,
t1.date,
CASE WHEN DATEDIFF('year', t1.date, CURRENT_DATE) < 2 THEN 1 ELSE 0 END as flag
from table t1;
if the "date" in fact is an integer:
select
t1.key,
t1.date,
CASE WHEN EXTRACT(YEAR FROM CURRENT_DATE) - t1.date < 2 THEN 1 ELSE 0 END as flag
from table t1;
Hope it helps
Sérgio

Related

max latest null value sql

I am experiencing the following problem. I have a table in this table I same history. Due to an error, I'm interested in finding the following information.
The latest record for a user where column1 value is null and the modifiedon date is the newest for this user. The problem is the table contains more records where the modifiedon is not null for this user and mutated after the date I'm looking for.
Can someone please point me in the right direction?
Sample data:
personid FreeField01 ModifiedOn
1 0004998 15-10-2019 11:48:19
1 NULL 20-10-2019 01:53:39
1 0004998 22-10-2019 14:58:44
1 0004998 22-10-2019 14:58:44
1 NULL 23-10-2019 07:52:46
1 0004998 23-10-2019 17:16:45
So for this user, I'm not interested in any record and should be excluded from the result because the modified on datetime should be before 29-10 and before that date the freefield01 value should be null and modifiedon should be the latest.
Three conditions:
There is no newer entry for the person.
The entry value is NULL.
The date is before 2019-10-29.
The query:
select *
from mytable
where not exists
(
select *
from mytable newer
where newer.personid = mytable.personid
and newer.modifiedon > mytable.modifiedon
)
and freefield01 is null
and modifiedon < date '2019-10-29'
order by personid;
You can use this below script-
WITH CTE
AS(
SELECT personid,MAX(ModifiedOn) MD
FROM your_table
GROUP BY personid
HAVING MAX(ModifiedOn) < '30-10-2019'
)
SELECT * FROM your_table A
INNER JOIN CTE B ON A.personid = B.personid
AND A.ModifiedOn = B.MD
AND A.FreeField01 IS NULL
DEMO HERE
If I understand correctly, you are looking for persons where the FreeField01 has a value of NULL as of a certain date.
Here is one method:
select t.*
from t
where t.ModifiedOn = (select max(t2.ModifiedOn)
from t t2
where t2.personid = t.personid and
t2.ModifiedOn <= '2019-10-29'
) and
t.FreeField01 is null;
EDIT:
Based on your comment, you might just want an aggregation and having:
select personid
from t
where t.ModifiedOn <= '2019-10-29'
group by person_id
having sum(case when t.FreeField01 is null then 1 else 0 end) = 0
The simplest query that I found might be the following if I understand your request well :
SELECT t.personid, t.FreeField01, MAX(ModifiedOn) FROM test t
GROUP BY personid
HAVING MAX(ModifiedOn) < '29-10-2019' AND FreeField01 IS NULL
SEE EXAMPLE HERE
EDIT : Following below suggestions you can use this query instead :
SELECT t1.personid, t1.FreeField01, t1.ModifiedOn
FROM test t1
JOIN (
SELECT t.personid, MAX(ModifiedOn) AS MaxModifiedOn FROM test t
GROUP BY personid
HAVING MAX(ModifiedOn) < STR_TO_DATE('29-10-2019','%d-%m-%Y')
) t2 ON (t1.personid = t2.personid AND t1.ModifiedOn = t2.MaxModifiedOn)
WHERE FreeField01 IS NULL
SEE NEW DEMO HERE

Query to search for 2 years and exclude 1

My apologies for the oddly worded question as I wasn't quite sure how I would name the title without explaining the situation.
I am currently working with a vendor table which gives a unique ID to each vendor, but the table is not normalized.
For example the ID 100000003744450 appears multiple times in the table with a different data in each row.
There are many columns but the only ones that matter to me at the moment are the ID and the year column. I am attempting to find the vendors who have rows for 2013, 2014 but not 2015.
So far I have:
select *
from table
where ls_d_yr = '2013'
or ls_d_yr = '2014'
I need to filter this results by removing any of the vendors that have the year 2013/2014 and should not have any rows with 2015 listed.
Here are the column
If one of 2013 and 2014, use NOT EXISTS to exclude ID's having ls_d_yr in 2015.
select *
from table t1
where ls_d_yr IN ('2013', '2014')
and not exists (select 1 from table t2
where t2.ID = t1.ID
and t2.ls_d_yr = '2015')
If both 2013 and 2014 are required, add a GROUP BY and use HAVING to make sure two different years are provided:
select ID
from table t1
where ls_d_yr IN ('2013', '2014')
and not exists (select 1 from table t2
where t2.ID = t1.ID
and t2.ls_d_yr = '2015')
group by ID
having count(distinct ls_d_yr) = 2
You can use NOT EXISTS for this:
select *
from table AS t1
where ls_d_yr IN ('2013', '2014') AND
NOT EXISTS (SELECT 1
FROM table AS t2
WHERE t1.ID = t2.ID AND ls_d_yr = '2015')
Another variation, should work in both Teradata and Aster (and probably every other DBMS):
select vendor
from table
where ls_d_yr in ('2013','2014','2015') -- probably numbers instead of strings?
group by vendor
having min(ls_d_yr) = '2013' -- at least one row from 2013
and max(ls_d_yr) = '2014' -- at least one row from 2014, but none from 2015
One method for doing this uses aggregation and having:
select t.vendor
from table t
group by t.vendor
having sum(case when ls_d_yr = '2013' then 1 else 0 end) > 0 and
sum(case when ls_d_yr = '2014' then 1 else 0 end) > 0 and
sum(case when ls_d_yr = '2015' then 1 else 0 end) = 0;
Each condition in the having clause tests for one year. The > 0 means that one or more records exist for the year. The = 0 means that no record exists.
This logic is based on the statement: "I am attempting to find the vendors who have rows for 2013, 2014 but not 2015." I don't follow the logic in the last paragraph.
select to_char(id), ls_d_yr
from table
where ls_d_yr like '%2014%'
or ls_d_yr like '%2013%';
something like that.

results of a sub table in the top level query

Not sure how to title this so please feel free to retitle.
I have two tables with a one to many relationship.
Table1
|ID|NAME|...|
Table2
|ID|Table1_ID|StartDate|EndDate|
I am trying to write a query that given a date will return the following
|TABLE1.ID|TABLE1.NAME|are any rows of table 2 in date|
I have a one to many between table 1 and table 2. I want to pass in a date to the query. If any of the many relationships in table 2 have a start date < passed in date and an end date > passed in date or end date is null then I want column 3 of result to be true. Otherwide I want it to be false.
Consider the example
|ID|NAME|...|
| 1|APPLE| ...|
| 2|PEAR| ...|
Table2
|ID|Table1_ID|StartDate|EndDate|
|1|1|01-01-2014|null|
|2|1|01-01-2014|01-02-2014|
|3|2|01-01-2014|01-02-2014|
if I pass in 01-01-2014 then I expect two rows with IDs 1 and 2 and both to be true (all rows match)
if I pass in 01-03-2014 then I expect two rows with ID 1 true (match on first row) and ID 2 to be false (because third row is outside of this date)
I am trying to do this in SQL to eventually convert to JPA. If there are any JPA functions that can do this then that would be good to know. Else I'll do a native query
Any pointers would be great!
Thanks
This should give you what you want:
select x.*, 'PASS' as checker
from table1 x
where exists
(select 'x'
from table2 y
where y.table1_id = x.table1_id
and y.startdate <= '01-01-2014'
and (y.enddate >= '01-01-2014' or y.enddate is null))
union all
select x.*, 'FAIL' as checker
from table1 x
where not exists
(select 'x'
from table2 y
where y.table1_id = x.table1_id
and y.startdate <= '01-01-2014'
and (y.enddate >= '01-01-2014' or y.enddate is null))
I don't know if I understand your question.
So, please, be patient... ;)
Try something like this:
select t1.id, t1.name,
case when t2.Table1_ID is null
then 'false'
else 'true' end as boolean_value
from Table1 t1,
(select distinct Table1_ID
from Table2
where yourdate >= StartDate
and (yourdate <= EndDate or EndDate is null) t2
where t1.id = t2.id (+);

PostgreSQL query with conditional empty values depending on preceding rows

I am working on a postgresql query that i am not sure how to produce the output.
Lets say i have a sql query whose output i want is
name date visit_number visit
x 2011-01-01 123 ?? (value i want=1)
y 2011-01-01 123 ?? (value i want=empty)
a 2011-02-02 345 ?? (value i want=1)
b 2011-02-02 345 ?? (empty)
c 2011-02-02 345 ?? (empty)
currently my sql query contains all the values except the last column visit. I want the visit column to work this way...if visit_number contains same value for multiple rows, i want the column visit to show the value 1 for the first row and just null or empty for the remaining rows where the visit_number is the same. How do i do that???
i could write the sample query in any way.it could simply be :
select name,date,visit_number from sometable order by date;
I am using postgres 8.1 version.
Thanks
The first thing you should do is upgrade to a modern day version of PostgreSQL. Version 8.1 has reached end of life in November 2010.
In a more recent version you can conveniently solve this with window functions:
SELECT name, date, visit_number
, CASE WHEN row_number() OVER (PARTITION BY visit_number
ORDER BY date, name) = 1
THEN 1
ELSE NULL
END AS visit
FROM tbl
ORDER BY date, name;
I ordered by name additionally to break ties.
For versions before PostgreSQL 8.4, this query should work (untested):
SELECT name, date, visit_number
, CASE WHEN EXISTS (
SELECT *
FROM tbl t1
WHERE t1.visit_number = tbl.visit_number -- more to make it unique?
AND t1.date <= tbl.date -- or more columns to make order unambiguous
AND t1.name < tbl.name
)
THEN NULL ELSE 1 END AS visit
FROM tbl
ORDER BY date, name;
This is the query:
select *,
case when row_number() over (partition by visit_number) = 1
then 1
else null
end
from t
Here is an example
Edit:
Without window function:
select t4.*, case when t3.name is not null then 1 end as visit from t t4
left join (
select t1.* from t t1
left join t t2 on t1.name > t2.name and t1.date = t2.date and
t1.visit_number = t2.visit_number
where t2.name is null
) as t3
on t3.name = t4.name and t3.date = t4.date and t3.visit_number = t4.visit_number
Here is an example
NOTE: If name is a key then the last comparison t3.date = t4.date and t3.visit_number = t4.visit_number can be removed

How do I use the value from row above when a given column value is zero?

I have a table of items by date (each row is a new date). I am drawing out a value from another column D. I need it to replace 0s though. I need the following logic: when D=0 for that date, use the value in column D from the date prior.
Actually, truth be told, I need it to say, when D is 0, use the value from the latest date where D was not a 0, but the first will get me most of the way there.
Is there a way to build this logic? Maybe a CTE?
Thank you very much.
PS I'm using SSMS 2008.
EDIT: I wasn't very clear at first. The value I want to change is not the date. I want change the value in D with the latest non-zero value from D, based on date.
May be the following query might help you. It uses the OUTER APPLY to fetch the results. Screenshot #1 shows the sample data and query output against the sample data. This query can be written better but this is what I could come up with right now.
Hope that helps.
SELECT ITM.Id
, COALESCE(DAT.New_D, ITM.D) AS D
, ITM.DateValue
FROM dbo.Items ITM
OUTER APPLY (
SELECT
TOP 1 D AS New_D
FROM dbo.Items DAT
WHERE DAT.DateValue < ITM.DateValue
AND DAT.D <> 0
AND ITM.D = 0
ORDER BY DAT.DateValue DESC
) DAT
Screenshot #1:
UPDATE t
Set value = SELECT value
FROM table
WHERE date = (SELECT MAX(t1.date)
FROM table t1
WHERE t1.value != 0
AND t1.date < t.date)
FROM table t
WHERE t.value = 0
You could maybe something like this as part of an update script...
SET myTable.D = (
SELECT TOP 1 myTable2.D
FROM myTable2
WHERE myTable2.myDateField < myTable.myDateField
AND myTable2.D != 0
ORDER BY myTable2.myDateField DESC)
That's assuming that you want to actually update the data though rather than just replace the values for the purpose of a select query.
How about:
SELECT
i.ID,
i.DateValue,
D = CASE WHEN I.D <> 0 THEN I.D ELSE X.D END
FROM
Items I
OUTER APPLY (
SELECT TOP 1 S.D
FROM Items S
WHERE S.DATEVALUE < I.DATEVALUE AND S.D <> 0
ORDER BY S.DATEVALUE DESC
) X
SELECT t.id,
CASE WHEN t.D = 0 THEN t0.D
ELSE t.D END
FROM table AS t
LEFT JOIN table AS t0
ON t0.time =
(
SELECT MAX(time) FROM t0
WHERE t0.time < t.time
AND t0.D != 0
)
or if you want to avoid aggregates entirely,
SELECT t.id,
CASE WHEN t.D = 0 THEN t0.D
ELSE t.D END
FROM table AS t
LEFT JOIN table AS t0
ON t0.time < t.time
LEFT JOIN table AS tx
ON tx.time > t0.time
WHERE t0.D != 0
AND tx.D != 0
AND tx.id IS NULL -- i.e. there isn't any