results of a sub table in the top level query - sql

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 (+);

Related

Best way in SQL to eliminate result rows with the same $ value but a day apart

I have an Oracle table that looks like the following
Region
Location
$ Amount
Date
Name
1
Location1
500
2021-01-01
UserA
1
Location1
-500
2021-01-02
UserA
2
Location2
700
2021-01-03
UserB
I want to find and remove the rows where the columns all match except the date is 1 day off and the $ amounts are opposite. In this example I would want to remove rows 1 and 2 because they are opposite $ amounts and only one day off.
Appreciate any help
You can use:
DELETE FROM table_name t
WHERE EXISTS (
SELECT 1
FROM table_name x
WHERE x.region = t.region
AND x.location = t.location
AND x.amount = -t.amount
AND x."DATE" IN (t."DATE" - 1, t."DATE" + 1)
AND x.name = t.name
)
Note: DATE is a keyword and you cannot use it for a column name unless you use a quoted identifier.
db<>fiddle here
This does what you describe:
select t.*
from t
where not exists (select 1
from t t2
where t2.region = t.region and
t2.location = t.location and
t2.name = t.name and
t2.amount = - t.amount and
t2.date in (t.date - 1, t.date + 1)
);
However, you might want to think about how you phrased the problem. What happens if you have 100/-100/100? The logic you describe (and the above query) will remove all three rows. That might be what you intend. If it is not, though, you should ask a new question.
Note: The above removes the rows from a result set. It is easy to modify this for a delete.

Update column in table depending on table data

I am working on a table on SQL where I need to fill in a new column depending on the value of 2 other columns in the table.
The rules are:
- New column = 1 if Row id = 1 and the group id for that row has more rows with the same group id (See pic)
- New column = 2 if Row id >= 2
- Anything else will make the new column 0
Table
This is my query to update the new column which works:
UPDATE t1 SET t1.New_Column = CASE WHEN t1.RowID >= 2 THEN 2
ELSE CASE WHEN t1.RowID = 1 and (SELECT count(*) FROM dbo.__test1 as t2 WHERE t2.GroupID = t1.GroupID) > 1
THEN 1 ELSE 0 END END from dbo.__test1 as t1
The end table result(Correct):
I want to know if there is a better and more efficient way of updating the new column as I feel calling the table within the select to count the number of group id's doesn't seem right?
Thanks
It will be faster to get the counts at the table level instead of row.
Removed one level of your CASE expression too - that probably won't change anything but it's a little cleaner to my eyes.
UPDATE t
SET t.NewColumn = CASE WHEN t2.RowID >=2 THEN 2
WHEN t2.RowID = 1 AND cnt > 1 THEN 1
ELSE 0
END
FROM dbo.__test1 t
JOIN (SELECT GroupID, COUNT(*) cnt
FROM dbo.__test1
GROUP BY GroupID) t2 on t.GroupID = t2.GroupID

Flag on condition

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

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