Assistance with SQL Query in Oracle - sql

I heed your help with the following:
I have a table like this:
Table_Values
ID | Value | Date
1 | ASD | 01-Jan-2019
2 | ZXC | 10-Jan-2019
3 | ASD | 01-Jan-2019
4 | QWE | 05-Jan-2019
5 | RTY | 15-Jan-2019
6 | QWE | 29-Jan-2019
That I need is to get the values that are duplicated and have a different Date, for example the value "QWE" is duplicated and has different date:
ID | Value | Date
4 | QWE | 05-Jan-2019
6 | QWE | 29-Jan-2019

With EXISTS:
select * from Table_Values t
where exists (
select 1 from Table_Values
where value = t.value and date <> t.date
)

Using Join:
select
t1.*
from
Table_Values t1
join
Table_Values t2
on t1.Value = t2.Value
and t1.Date <> t2.Date
However, Exists approach is better.

You want all rows where there is more than one date per value. You can use COUNT OVER for this.
One method (featured as of Oracle 12c):
select id, value, date
from mytable
order by case when count(distinct date) over (partition by value) > 1 then 1 else 2 end
fetch first row with ties
But you'll have to put this into a subquery (derived table / cte), if you want the result sorted.
And another method without FETCH FIRST clause (valid as of Oracle 8i):
select id, value, date
from
(
select id, value, date, count(distinct date) over (partition by value) as cnt
from mytable
)
where cnt > 1
order by id, value, date;
forpas' solution with EXISTS may be faster, though. Well, pick whichever method you like better :-)

With EXISTS, "correlated subquery" is used. So I don't think it's better than JOIN.
However, Oracle optimizer could re-write "EXISTS" to JOIN.
I like to use JOIN in classic way :)
SELECT t1.*
FROM table_values t1, table_values t2
WHERE t1.f_value = t2.f_value
AND t1.f_date <> t2.f_date
ORDER BY 1;

Related

SQL statement for current line out of log table

I have a table with four columns: Type, SubT, Info and Timestamp.
Type and SubT are type and subtype of the entry, Info is a string which is different foe each row and Timestamp the day/time when the entry happened.
I search a SQL statement which gives me in one query the current (with respect to Timestamp) rows for each Type and SubT including all other columns.
So, with this data:
Type | SubT | Info | Timestamp
-----+------+-------------------+--------------
1 | 2 | Hello | 20190223T1300
1 | 3 | Fuuuu | 20190223T1301
1 | 3 | Baaar | 20190223T1400
3 | 2 | Something | 20190222T1300
I would like to get that result:
Type | SubT | Info | Timestamp
-----+------+-------------------+--------------
1 | 2 | Hello | 20190223T1300
1 | 3 | Baaar | 20190223T1400
3 | 2 | Something | 20190222T1300
In other words:
All columns for each existing combination of Type and SubT and if there are several rows for one Type/SubT, then the one with the youngest timestamp.
To generalize: there could be more then two "distinct" columns like Type and SubT and there could be more then one column like info which is taken from the row with the most current timestamp.
Any suggestions?
You can use below.
select t1.* from table_name t1
inner join (select type, subt, min(timestamp) group by type, sub) a
on (t1.type = t2.type and t1.subt = t2.subt);
I simple method is a correlated subquery in the where clause:
select t.*
from t
where t.timestamp = (select max(t2.timestamp)
from t t2
where t2.type = t.type and t2.subt = t.subt
);
In many databases, this will be a highly performant solution, particularly with an index on (type, subt, timestamp desc).
use corelated subwuery
select t1.* from table t1
where t1.Timestamp=( select max(Timestamp) from table t2
where t1.SubT =t2.SubT and t1.type=t2.type
)
or row_number()which support most dbms
select * from
(
select *,row_number()over(partition by subT,type order by Timestamp desc) rn
from table
) t where t.rn=1

PostgreSQL - MAX value for every user

I have a table
User | Phone | Value
Peter | 0 | 1
Peter | 456 | 2
Peter | 456 | 3
Paul | 456 | 7
Paul | 789 | 10
I want to select MAX value for every user, than it also lower than a tresshold
For tresshold 8, I want result to be
Peter | 456 | 3
Paul | 456 | 7
I have tried the GROUP BY with HAVING, but I am getting
column "phone" must appear in the GROUP BY clause or be used in an aggregate function
Similar query logic works in MySQL, but I am not quite sure how to operate with GROUP BY in PostgreSQL. I dont want to GROUP BY phone.
After I have results from "juergen d" solution, I came up with this which gives me the same results faster
SELECT DISTINCT ON(user) user, phone, value
FROM table
WHERE value < 8
ORDER BY user, value DESC;
select t1.*
from your_table t1
join
(
select user, max(value) as max_value
from your_table
where value < 8
group by user
) t2 on t1.user = t2.user and t1.value = t2.max_value
Alternatively, you could use a ranking function:
select * from
(
select *, RANK() OVER (partition by [user] ORDER BY t.value desc ) as value_rank from test_table as t
where t.value < 8
) as t1
where value_rank = 1

How to get a single result with columns from multiple records in a single table?

Platform: Oracle 10g
I have a table (let's call it t1) like this:
ID | FK_ID | SOME_VALUE | SOME_DATE
----+-------+------------+-----------
1 | 101 | 10 | 1-JAN-2013
2 | 101 | 20 | 1-JAN-2014
3 | 101 | 30 | 1-JAN-2015
4 | 102 | 150 | 1-JAN-2013
5 | 102 | 250 | 1-JAN-2014
6 | 102 | 350 | 1-JAN-2015
For each FK_ID I wish to show a single result showing the two most recent SOME_VALUEs. That is:
FK_ID | CURRENT | PREVIOUS
------+---------+---------
101 | 30 | 20
102 | 350 | 250
There is another table (lets call it t2) for the FK_ID, and it is here that there is a reference
saying which is the 'CURRENT' record. So a table like:
ID | FK_CURRENT | OTHER_FIELDS
----+------------+-------------
101 | 3 | ...
102 | 6 | ...
I was attempting this with a flawed sub query join along the lines of:
SELECT id, curr.some_value as current, prev.some_value as previous FROM t2
JOIN t1 curr ON t2.fk_current = t1.id
JOIN t1 prev ON t1.id = (
SELECT * FROM (
SELECT id FROM (
SELECT id, ROW_NUMBER() OVER (ORDER BY SOME_DATE DESC) as rno FROM t1
WHERE t1.fk_id = t2.id
) WHERE rno = 2
)
)
However the t1.fk_id = t2.id is flawed (i.e. wont run), as (I now know) you can't pass a parent
field value into a sub query more than one level deep.
Then I started wondering if Common Table Expressions (CTE) are the tool for this, but then I've no
experience using these (so would like to know I'm not going down the wrong track attempting to use them - if that is the tool).
So I guess the key complexity that is tripping me up is:
Determining the previous value by ordering, but while limiting it to the first record (and not the whole table). (Hence the somewhat convoluted sub query attempt.)
Otherwise, I can just write some code to first execute a query to get the 'current' value, and then
execute a second query to get the 'previous' - but I'd love to know how to solve this with a single
SQL query as it seems this would be a common enough thing to do (sure is with the DB I need to work
with).
Thanks!
Try an approach with LAG function:
SELECT FK_ID ,
SOME_VALUE as "CURRENT",
PREV_VALUE as Previous
FROM (
SELECT t1.*,
lag( some_value ) over (partition by fk_id order by some_date ) prev_value
FROM t1
) x
JOIN t2 on t2.id = x.fk_id
and t2.fk_current = x.id
Demo: http://sqlfiddle.com/#!4/d3e640/15
Try out this:
select t1.FK_ID ,t1.SOME_VALUE as CURRENT,
(select SOME_VALUE from t1 where p1.id2=t1.id and t1.fk_id=p1.fk_id) as PREVIOUS
from t1 inner join
(
select t1.fk_id, max(t1.id) as id1,max(t1.id)-1 as id2 from t1 group by t1.FK_ID
) as p1 on t1.id=p1.id1

Distinct Value of a column in sql server 2008

Hello all I have made a query using left outer joins which result in some what like the table below:
| 00-00-00-00-00 | 1 | a.txt |
| 00-00-00-00-00 | 2 | b.txt |
| 00-00-00-00-00 | 1 | c.txt |
| 11-11-11-11-11 | 2 | d.txt |
What I want is Distict value of the MAC Column below is the SQL Fiddle to understand better.
SQLFIDDLE
Thanks
EDIT
The purpose is that 2 and 3 are useless or redundant data where as 1 and 4 are useful means the 1 and 4 show the current file on the MACs
Output:
| 00-00-00-00-00 | 1 | a.txt |
| 11-11-11-11-11 | 2 | d.txt |
Is not possible to answer exactly what you ask. However, usually folk that express the question you ask really mean to ask something like 'I want all the columns for a sample of rows containing only distinct MacAddress values'. This question has many answers, as the result is non-deterministic. A trivial solution is to pick the first (for whatever definition of 'first') row for each MacAddress:
with cte as (
select row_number() over (partition by MacAddress order by CounterNo) as rn, *
from Heartbeats
)
select * from cte where rn = 1;
If you want to get only the distinct macaddresses, you can do:
SELECT DISTINCT macaddress FROM heartbeats
If you want all the columns alongside the distinct macaddress, you need to create a rule to get them. The query below gives you the ones with the highest id for each macaddress:
SELECT t1.*
FROM heartbeats t1
LEFT JOIN heartbeats t2
ON (t1.macaddress = t2.macaddress AND t1.id < t2.id)
WHERE t2.id IS NULL
sqlfiddle demo
EDIT:
Since in original query the code used doesnt have ID column the above query was refined as:
with cte as (
select ROW_NUMBER() OVER(ORDER BY (Select 0)) AS ID,* from heartBeats
)
SELECT t1.*
FROM cte t1
LEFT JOIN cte t2
ON (t1.macaddress = t2.macaddress AND t1.id < t2.id)
WHERE t2.id IS NULL
SQL Fiddle
SELECT hb1.* FROM [heartbeats] as hb1
LEFT OUTER JOIN [heartbeats] as hb2
ON (hb1.macaddress = hb2.macaddress AND hb1.id > hb2.id)
WHERE hb2.id IS NULL;
You have to neglect the file name. See http://sqlfiddle.com/#!3/a75e47/13

Check multiple rows for value, return only row with MAX/MIN

I'm trying to write a query that will compare the value of N amount of rows and return only the row with the max value. For example, if I wanted to only return a table with non-duplicate rows, but only the row with the newest date -
key | name | value | date
1 | frank | 100 | 1/1/2013
2 | peter | 200 | 2/1/2013
3 | jonny | 300 | 3/1/2013
4 | jonny | 300 | 4/1/2013
And the query:
SELECT key, name, value, MAX(date)
FROM myTable
WHERE key IN (1,2,3,4)
I'd be expecting this to return
key | name | value | date
1 | frank | 100 | 1/1/2013
2 | peter | 200 | 2/1/2013
4 | jonny | 300 | 4/1/2013
I am unsure how to use GROUP BY, I think I'm missing something fundamental with my attempts at it.
Well if you only want the newest row you could use the following:
SELECT TOP 1 key, name, value, date
FROM myTable
ORDER BY date desc
This should return the one row with the newest date in that table.
If you wanted the newest date for each name you could use group by:
SELECT name, max(date)
FROM myTable
WHERE key in(1,2,3,4)
GROUP BY name
Max is an aggregate function. Anytime you use an aggregate function any columns that are not being aggregated have to be specified in the group by clause.
So based on your expected results you probably want this:
;with namesWithMaxDate as(
select
name
,max(date) as date
from
myTable
group by
name
)
select
myTable.[key]
,myTable.name
,myTable.value
,myTable.date
from myTable
inner join
namesWithMaxDate
on
myTable.name = namesWithMaxDate.name and
myTable.date = namesWithMaxDate.date
This is slightly more complex because you have columns that you want returned that are not included in the grouping. Hence two statements to arrive at the final result set.
Final option: good old fashioned sub-query.
select
myTable.[key]
,myTable.name
,myTable.value
,myTable.date
from myTable
inner join
( select
name
,max(date) as date
from
myTable
group by
name ) as namesWithMaxDate
on
myTable.name = namesWithMaxDate.name and
myTable.date = namesWithMaxDate.date
More here about aggregate functions.
More here about group by.
Try This one:
SELECT a.key, a.name, a.value, a.date
FROM myTable a
WHERE a.key IN (1,2,3,4)
and
a.DATE = (select MAX(date) from myTable b where a.key = b.key)