Return based on values selected - sql

I really don't know how to phrase the title correctly, so please excuse me if the title is confusing.
Here's the scenario I am facing:
I have a table...assuming it contains the follow rows.
Name | Value
----- | ----
John | 1
Mary | 2
Jack | 3
Jim | 4
Here's the PL/SQL requirement:
If John exists, return John and his value.
If John does not exist, but Mary does, return Mary and her value.
If neither John nor Mary exist, return either Jack or Jim whichever
has the higher value.
I was able to use cursor to traverse the table and test each row, but am wondering if there are other more efficient way.
Thanks!

No need for a cursor and a loop. You can do this in a single query, using a conditional sort and a fetch clause:
select *
from mytable
order by
case name when 'John' then 1 when 'Mary' then 2 else 3 end,
value desc
fetch first row only
Or if you are a pre-12c version of Oracle, where the fetch clause is not available:
select name, value
from (
select t.*,
row_number() over(order by
case name when 'John' then 1 when 'Mary' then 2 else 3 end,
value desc
) rn
from mytable t
) t
where rn = 1

Related

How to compare rows with each other and keep only one row according to a condition

I have a requirement as per below:
IF More than 1 comment exist (group of name, lastname and door) and one of them includes NULL then keep only the record with the Null comment and discard the others.
IF Null IS NOT one of them and the comment includes NOT AVAILABLE and REQUIRES. Keep Not available - discard REQUIRES
IF all of them have only REQUIRES choose the record with the lowest amount or value.
Name Lastname Comment Amount Door
John R. NULL 250 1
John R. NULL 250 1
John R. New design is available 250 1
John W. Not available 250 2
John W. Requires additional comment 450 2
John S. Requires further explanation 200 3
John S. Requires more information 300 3
Result should come like below:
Name Lastname Comment Amount Door
John R. NULL 250 1
John W. Not available 250 2
John S. Requires further explanation 200 3
I am trying to write a CTE to get the result but not sure how to compare the comment section something like below:
WITH RNs AS(
SELECT name,
lastname,
door,
package,
DENSE_RANK() OVER (PARTITION BY name ORDER BY door ASC) AS RN
FROM test)
SELECT distinct name,
lastname,
door,package,
CASE when package IS NULL THEN 'PASS'
when package like 'Not available%' then 'PASS'
when package like 'requires%' then 'PASS' else 'fail' END AS to_keep_or_not
FROM RNs
GROUP BY RN,
name,
lastname,
door,package;
What would be the best approach to solve this kind of problem statement?
Please try below query:
with cte as (
select name ,lastname,comment,amount,door ,row_number()over(partition by name ,lastname order by comment ,(case when comment like '%Requires%' then amount else 0 end)) rn from testreq
where comment is null or comment like '%Not available%' or comment like '%Requires%')
select Name,lastname,comment,amount,door from cte where rn=1
order by door
Output:
|name|lastname|comment |amount|door|
|----|--------|----------------------------|------|----|
|John|R. |NULL |250 |1 |
|John|W. |Not available |250 |2 |
|John|S. |Requires further explanation|200 |3 |
You want an order by with a case expression . . . along with ROW_NUMBER():
SELECT t.*
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY name, lastname
ORDER BY (CASE WHEN comment IS NULL THEN 1
WHEN comment LIKE '%NOT AVAILABLE%' THEN 2
ELSE 3
END),
amount
ORDER BY door ASC
) as seqnum
FROM test t
) t
WHERE seqnum = 1;

Join two rows that contain a common column value [duplicate]

Let's say I've got the following database table
Name | Nickname | ID
----------------------
Joe Joey 14
Joe null 14
Now I want to do a select statement that merges these two columns to one while replacing the null values. The result should look like this:
Joe, Joey, 14
Which sql statement manages this (if it's even possible)?
Simplest solution:
SQL> select * from t69
2 /
NAME NICKNAME ID
---------- ---------- ----------
Joe Joey 14
Joe 14
Michael 15
Mick 15
Mickey 15
SQL> select max(name) as name
2 , max(nickname) as nickname
3 , id
4 from t69
5 group by id
6 /
NAME NICKNAME ID
---------- ---------- ----------
Joe Joey 14
Michael Mickey 15
SQL>
If you have 11gR2 you could use the new-fangled LISTAGG() function but otherwise it is simple enough to wrap the above statement in a SELECT which concatenates the NAME and NICKNAME columns.
AFAIK,
the question is not clear.so i am making some assumptions over here.
your output has the first and 3rd columns for both the rows as same.
Only the 2nd field is different.
so u can simply write a select quest
select one.name,two.nick_name,one.id from
(select name,id from your_tb group by name,id) one,
your_tb two
where two.nickname is not NULL
and two.name=one.name
and two.id=one.id;
may be we can tune this but i am not good in tuning sql squeries,but this is the way i suppose u need.

How to query: "for which do these values apply"?

I'm trying to match and align data, or resaid, count occurrences and then list for which values those occurrences occur.
Or, in a question: "How many times does each ID value occur, and for what names?"
For example, with this input
Name ID
-------------
jim 123
jim 234
jim 345
john 123
john 345
jane 234
jane 345
jan 45678
I want the output to be:
count ID name name name
------------------------------------
3 345 jim john jane
2 123 jim john
2 234 jim jane
1 45678 jan
Or similarly, the input could be (noticing that the ID values are not aligned),
jim john jane jan
----------------------------
123 345 234 45678
234 123 345
345
but that seems to complicate things.
As close as I am to the desired results is in SQL, as
for ID, count(ID)
from table
group by (ID)
order by count desc
which outputs
ID count
------------
345 3
123 2
234 2
45678 1
I'll appreciate help.
You seem to want a pivot. In SQL, you have to specify the number of columns in advance (unless you construct the query as a string).
But the idea is:
select ID, count(*) as cnt,
max(case when seqnum = 1 then name end) as name_1,
max(case when seqnum = 2 then name end) as name_2,
max(case when seqnum = 3 then name end) as name_3
from (select t.*,
row_number() over (partition by id order by id) as seqnum -- arbitrary ordering
from table t
) t
group by ID
order by count desc;
If you have an unknown number of columns, you can aggregate the values into an array:
select ID, count(*) as cnt,
array_agg(name order by name) as names
from table t
group by ID
order by count desc
the query would look similar to this if that's what you're looking for.
SELECT
name,
id,
COUNT(id) as count
FROM
dataSet
WHERE
dataSet.name = 'input'
AND dataSet.id = 'input'
GROUP BY
name,
id

Limit column value repeats to top 2

So I have this query:
SELECT
Search.USER_ID,
Search.SEARCH_TERM,
COUNT(*) AS Search.count
FROM Search
GROUP BY 1,2
ORDER BY 3 DESC
Which returns a response that looks like this:
USER_ID SEARCH_TERM count
bob dog 50
bob cat 45
sally cat 38
john mouse 30
sally turtle 10
sally lion 5
john zebra 3
john leopard 1
And my question is: How would I change the query, so that it only returns the top 2 most-searched-for-terms for any given user? So in the example above, the last row for Sally would be dropped, and the last row for John would also be dropped, leaving a total of 6 rows; 2 for each user, like so:
USER_ID SEARCH_TERM count
bob dog 50
bob cat 45
sally cat 38
john mouse 30
sally turtle 10
john zebra 3
In SQL Server, you can put the original query into a CTE, add the ROW_NUMBER() function. Then in the new main query, just add a WHERE clause to limit by the row number. Your query would look something like this:
;WITH OriginalQuery AS
(
SELECT
s.[User_id]
,s.Search_Term
,COUNT(*) AS 'count'
,ROW_NUMBER() OVER (PARTITION BY s.[USER_ID] ORDER BY COUNT(*) DESC) AS rn
FROM Search s
GROUP BY s.[User_id], s.Search_Term
)
SELECT oq.User_id
,oq.Search_Term
,oq.count
FROM OriginalQuery oq
WHERE rn <= 2
ORDER BY oq.count DESC
EDIT: I specified SQL Server as the dbms I used here, but the above should be ANSI-compliant and work in Snowflake.

SQL field default "count(another_field) +1"

I need to create a field COUNT whose default value is the automatically generated count of times NAME has appeared in that table till now, as shown in example below. Since i am adding the field to an existing table, i also need to populate existing rows. How best to go about this please?
ID NAME COUNT
1 peter 1
2 jane 1
3 peter 2
4 peter 3
5 frank 1
6 jane 2
7 peter 4
You would do this when you are querying the table, using the ANSI-standard row-number function:
select id, name, row_number() over (partition by name order by id) as seqnum
from t;