duplicate values in a row using pl/sql - sql

i have this query:
SELECT distinct
num as number,
name as name
from my_table_name
where number = '12345';
And this is the results:
number - name
1. 12345 - mike
2. 12345 - charlie
3. 12345 - jose
I need a new query when this happens (numbers duplicate or triplicate) show me only one of them. Example:
number - name
12345 - mike
I only need one of them; the position doesn't matter. If it find one, print this and close the procedure, function or cursor.

Distinct is going to return results that are distinct, relative to all of the data you are querying for. If you only want one of the results returned and you know that the result used is arbitrary, you can just add a filter based on the row number (how specifically this is done depends on what DBMS you are using.)
Oracle example:
select num as "number",
name as "name"
from my_table_name
where number = '12345'
and rownum = 1; -- just gets the first row.

SELECT * from
(SELECT rownum rnum,
num as number,
name as name
FROM my_table_name
WHERE number = '12345' )
WHERE rnum = 1

use ROW_NUMBER analytic function
SELECT *
FROM
(
select id, name, ROW_NUMBER() OVER ( partition by id order by name asc) as seq
from tableA
where number = '12345'
) T
where T.seq =1

If you don't care which one is being returned, why are you even asking for it to be returned?
However, to get a single line of results regardless of the number of matching rows, you should probably be using GROUP BY and a summary function:
Select
num as number,
max(name) as name --or min(), or any other summary function that works on this data type
from my_table_name
where num = '12345'
group by num

Related

Getting MAX of a column and adding one more

I'm trying to make an SQL query that returns the greatest number from a column and its respective id.
For more information I have two columns ID and NUMBER. Both of them have 2 entries and I want to get the highest number with the ID next to it. This is what I tried but didn't success.
SELECT ID, MAX(NUMBER) AS MAXNUMB
FROM TABLE1
GROUP BY ID, MAXNUMB;
The problem I'm experiencing is that it just shows ALL the entries and if I add a "where" expression it just shows the same (all entries [ids+numbers]).
Pd.: Yes, I got what I wanted but only with one column (number) if I add another column (ID) to select it "brokes".
Try:
SELECT
ID,
A_NUMBER
FROM TABLE1
WHERE A_NUMBER = (
SELECT MAX(A_NUMBER)
FROM TABLE1);
Presuming you want the IDs* of the row with the highest number (and not, instead, the highest number for each ID -- if IDs were not unique in your table, for example).
* there may be more than one ID returned if there are two or more IDs with equal maximum numbers
you can try this
Select ID,maxNumber
From
(
SELECT
ID,
(Select Max(NUMBER) from Tmp where Id = t.Id) maxNumber
FROM
Tmp t
)T1
Group By ID,maxNumber
The query you posted has an illegal column name (number) and is group by the alias for the max value, which is illegal and also doesn't make sense; and you can't include the unaliased max() within the group-by either. So it's likely you're actually doing something like:
select id, max(numb) as maxnumb
from table1
group by id;
which will give one row per ID, with the maximum numb (which is the new name I've made up for your numeric column) for each ID. Or as you said you get "ALL the entries" you might have group by id, numb, which would show all rows from the table (unless there are duplicate combinations).
To get the maximum numb and the corresponding id you could group by id only, order by descending maxnumb, and then return the first row only:
select id, max(numb) as maxnumb
from table1
group by id
order by maxnumb desc
fetch first 1 row only
If there are two ID with the same maxnumb then you would only get one of them - and which one is indeterminate unless you modify the order by - but in that case you might prefer to use first 1 row with ties to see them all.
You could achieve the same thing with a subquery and analytic function to generating a ranking, and have the outer query return the highest-ranking row(s):
select id, numb as maxnumb
from (
select id, numb, dense_rank() over (order by numb desc) as rnk
from table1
)
where rnk = 1
You could also use keep to get the same result as first 1 row only:
select max(id) keep (dense_rank last order by numb) as id, max(numb) as maxnumb
from table1
fiddle

How to count and select records with count = 1 across multiple columns

Hi and thanks in advance.
Trying to simplify two SQL statements without much success.
Count unique records in a table (by unique, I mean the whole record is found only once in the table)
Select unique records in a table (by unique, I mean the whole record is found only once in the table)
Since the table has 30 columns, is there some way to simply specify ALL columns in the table rather than having to include all individually in the SQL?
I got this working where you spell out every column name (where 'col n name' refers to the last column) but it is not ideal since there are just so many columns …
SELECT col 1 name, col 2 name, col 3 name, …, col n name FROM table name
GROUP BY col 1 name, col 2 name, col 3 name, …, col n name
Having Count(*)=1
Thanks
deutz
create view tab_view1 as select DISTINCT * from tab;
select COUNT(*) from tab_view1; -- first desired result
select * from tab_view1; -- second desired result
First occurence is unique, second is not.
If you want to exclude any record that has duplicate:
create view tab_view2 as select tab.*, COUNT(*) AS occurs
from tab group by tab.*
having COUNT(*) = 1;
select COUNT(*) from tab_view2; -- first desired result
select * from tab_view2; -- second desired result

Permuting values in SQL

Let's say I have a table with two columns:
id | value
----------
1 | 101
2 | 356
3 | 28
I need to randomly permute the value column so that each id is randomly assigned a new value from the existing set {101,356,28}. How could I do this in Oracle SQL?
It may sound odd but this is a real problem, just with more columns.
You can do this by using row_number() with a random number generator and then joining back to the original rows:
with cte as (
select id, value,
row_number() over (order by id) as i,
row_number() over (order by dbms_random.random) as rand_i
from table t
)
select cte.id, cte1.value
from cte join
cte cte1
on cte.i = cte.rand_i;
This guarantees a permutation (i.e. no original row has its value used twice).
EDIT:
By the way, if the original ids are sequential from 1 and have no gaps, you could just do:
select row_number() over (order by dbms.random) as id, value
from table t;
An Option : select * from x_table where id = round(dbms_random.value() * 3) + 1; [Here 3 is the number of rows in your random data table and I am assuming that id is incremental and unique?]
I'll think of other options.
I'm not sure whether this is the right task for SQL database. Maybe you should implement something like this:
Factoradic permutation - in PL/SQL and then return a cursor via PIPE ROW construct. Ordering by dbms.random might be slow for large data sets.

Oracle query - select top records

Assuming the following table:
ID Name Revision
--- ----- --------
1 blah 0
2 yada 1
3 blah 1
4 yada 0
5 blah 2
6 blah 3
How do I get the two records, one for "blah" and one for "yada" with highest revision number (3 for blah and 1 for yada)? Something like:
ID Name Revision
--- ----- --------
6 blah 3
2 yada 1
Also, once these records are retrieved, how do I get the rest, ordered by name and revision?
I am trying to create a master-detail view where master records are latest revisions and details include the previous revisions.
Basically, with the aggregate function MAX():
SELECT "Name", MAX("Revision") AS max_revison
FROM tbl
WHERE "Name" IN ('blah', 'yada');
GROUP BY "Name"
ORDER BY "Name"; -- ordering by revision would be pointless;
If you need more columns from the row, there are several ways. One would be to join the above subquery back to the base table:
SELECT t.*
FROM (
SELECT "Name", max("Revision") AS max_revison
FROM tbl
WHERE "Name" IN ('blah', 'yada');
GROUP BY "Name"
) AS sub
JOIN tbl AS t ON t."Revision" = sub.max_revison
AND t."Name" = sub."Name"
ORDER BY "Name";
Generally, this has the potential to yield more than one row per "Name" - if "Revision" is not unique (per "Name"). You would have to define how to pick one from a group of peers sharing the same maximum "Revision" - a tiebreaker.
Another way would be with NOT EXISTS, excluding rows that have greater peers, possibly faster:
SELECT t.*
FROM tbl AS t
WHERE "Name" IN ('blah', 'yada')
AND NOT EXISTS (
SELECT 1
FROM tbl AS t1
WHERE t1."Name" = t."Name"
AND t1."Revision" > t."Revision"
)
ORDER BY "Name";
Or you could use a CTE with an analytic function (window function):
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY "Name" ORDER BY "Revision" DESC) AS rn
FROM tbl
WHERE "Name" IN ('blah', 'yada')
)
SELECT *
FROM cte
WHERE rn = 1;
The last one is slightly different: one row per "Name" is guaranteed. If you don't use more ORDER BY items, an arbitrary row will be picked in case of a tie. If you want all peers use RANK() instead.
This approach will select the rows for each Name with the maximum revision number for that Name. The result will be the exact output you were looking for in your post.
SELECT *
FROM tbl a
WHERE a.revision = (select max(revision) from tbl where name = a.name)
ORDER BY a.name
In Oracle, you can use LAST function to simplify this.
select max(id) keep (dense_rank last order by revision),
name,
max(revision)
from table
group by name;
Demo

`single-row subquery returns more than one row` error unclarity

I've got two tables.
The first one hold a type and the second one the value of this type.
In my example if there are more values 'john' as 'first_name' i get:single-row subquery returns more than one row
SELECT DISTINCT id FROM name WHERE id=(
SELECT id FROM name WHERE text1='first_name' INTERSECT
SELECT name_id FROM value WHERE text2='john');
I'm not very good with sql. I should use LEFT JOIN or something like that but it's not really clear to me how i should do this.
Since subquery [can] returns multiple values, IN is better use than =
SELECT DISTINCT id
FROM name
WHERE id IN (
SELECT id FROM name WHERE text1='first_name'
INTERSECT
SELECT name_id FROM value WHERE text2='john');
IN is equivalent for OR, example:
SELECT *
FROM tableName
WHERE a = 4 or a = 5 or a = 6
can be written as
SELECT *
FROM tableName
WHERE a in (4,5,6)
The = (equal sign) is used to assign single value.
Apart from the simple fix (using IN instead of id=), you can also use a somewhat simpler version of your query:
SELECT DISTINCT id FROM name WHERE text1='first_name'
and id in (
SELECT name_id FROM value WHERE text2='john')
SELECT id FROM name WHERE text1='first_name' INTERSECT
SELECT name_id FROM value WHERE text2='john');
This query returns more than one row, then you should use IN clause like here
SELECT DISTINCT id FROM name WHERE id IN (
SELECT id FROM name WHERE text1='first_name' INTERSECT
SELECT name_id FROM value WHERE text2='john');
Or if you expect only a row, you have to review your DB's logic and behaviour.
you could just change
WHERE id=
to
WHERE id IN