How to sort a same column both in ASC & DESC order without duplicates - sql

With an SQL Query how do we get the output of 2 columns, the first one being a column sorted in ASC order and the second one with the order DESC without any duplicate values and both are same columns.
EMP table:
EMP_NAME
Megha
Vicky
Neha
Sachin
Neha
The query output should be
EMP_NAME_ASC EMP_NAME_DESC
Megha Vicky
Neha Sachin
Sachin Neha
Vicky Megha
I tried below solution
How to sort a same column both in asc order and desc order
but that is not giving me distinct values.
Any help would be appreciated.
Thank you in advance.

With a little modification on Gordon Linoff's answer, you can get distinct names:
;WITH
cte AS
(
SELECT EMP_NAME
, DENSE_RANK() OVER (ORDER BY EMP_NAME ASC) AS RankAsc
, DENSE_RANK() OVER (ORDER BY EMP_NAME DESC) AS RankDesc
, ROW_NUMBER() OVER (PARTITION BY EMP_NAME ORDER BY EMP_NAME)
AS RowNumber
FROM #EMP
)
SELECT cte1.EMP_NAME AS EMP_NAME_ASC
, cte2.EMP_NAME AS EMP_NAME_DESC
FROM cte cte1
INNER JOIN cte cte2 ON cte1.RankAsc = cte2.RankDesc
WHERE cte1.RowNumber = 1
AND cte2.RowNumber = 1
The ROW_NUMBER gives a sequential number each time the same name appears. The first time Neha appears it will get 1, the second time it will get 2 and so on. We are interested in the first time each name appears and hence get the distinct names only.

You are two separate lists in columns. This is not a SQL-ish way to store data -- the rows don't mean anything. But you can do this, using row_number():
select a.name, d.name
from (select name, row_number() over (order by name asc) as seqnum
from t
) a join
(select name, row_number() over (order by name desc) as seqnum
from t
) d
on a.seqnum = d.seqnum;

Related

How to select only 3 records from multiple records in column?

I have table with 3 Columns (ID, CARDNO, CONTACTNO)
Each cardno have multiple contactno
ID CARDNO CONTACTNO
1 1234567895412 32225465987
2 1234567895412 65554789654
3 1234567895412 24445698741
4 1234567895412 24445698745
5 1234567895412 45556987123
I want only select 3 random contactno against 1 card
Well you have several options, but I think that the easiest one is:
SELECT *
FROM contact_data -- your table name
ORDER BY dbms_random.random
FETCH FIRST 3 ROWS ONLY;
You can use the ROW_NUMBER analytical function as follows:
SELECT ID, CARDNO, CONTACTNO FROM
(SELECT ID, CARDNO, CONTACTNO,
ROW_NUMBER() OVER (PARTITION BY CARDNO ORDER BY 1) AS RN
FROM YOUR_TABLE)
WHERE RN <= 3;
If you really need randomness then you can replace ORDER BY 1 with ORDER BY dbms_random.value(0,1) in the OVER clause of the ROW_NUMBER.
May be you can just try something like this
SELECT * FROM TABLE WHERE CARDNO = '1234567895412' ORDER BY RAND() LIMIT 3;
replace TABLE with your table name,
If you really want random then use row_number() with a dbms_random function:
select t.*
from (select t.*,
row_number() over (partition by cardno order by dbms_random.value) as seqnum
from t
) t
where seqnum <= 3;
Colloquially, though, people often use "random" but not in the technical sense. If that is the case, you can replace the order by with whatever makes sense -- including dropping it altogether.

How to to get maximum sequence number in SQL

This is the data I have in my table. What I want is maximum sequence number for each order number.
Order No seq Sta
--------------------
32100 1 rd
32100 3 rd
23600 1 rd
23600 6 rd
I want to get the following result without using cursor.
Output:
Order No seq Sta
-----------------
32100 3 rd
23600 6 rd
If you want entire records you could use ROW_NUMBER:
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY Order ORDER BY No_Seq DESC) AS rn
FROM tab) s
WHERE rn = 1;
DBFiddle Demo
Please do not use keywords like Order and spaces in column names.
The most simple solution is using group by with max.
Give this a try:
select [Order No], max(seq), Sta
from myTable
group by [Order No]
Just use group by order no and order by sequence desc and you will get your record.
If you are using Oracle Database then you can use ROW_NUMBER() analytical function to achieve this result
Try the below query:
select
*
from
(select
ROW_NUMBER() OVER (PARTITION BY order_no ORDER BY seq DESC) as "ROW_NUM",
order_no, seq, sta
from
Order_Details) temp
where
temp.row_num = 1 ;
Demo
The following is probably the most efficient solution in most databases (with the right index):
select t.*
from t
where t.seq = (select max(t2.seq) from t t2 where t2.orderno = t.orderno);
You can also do this with group by:
select orderno, max(seq), sta
from t
group by orderno, sta;
Note that all columns referenced in the select are either group by keys or arguments to aggregation functions. This is proper SQL.

Return two rows from SQL table with a difference in values [duplicate]

This question already has answers here:
How to request a random row in SQL?
(30 answers)
Closed 6 years ago.
iam trying to return 2 rows from table that have a difference in values, not being an SQL wise man i am stuck any help would be appreciated :-)
TABLE A:
NAME DATA
Oscar HOME1
Jens HOME2
Will HOME1
Jeremy HOME2
Al HOME1
Result, should be 2 random rows with a difference in DATA value
NAME DATA
Oscar HOME1
Jeremy HOME2
Anyone?
Easy way to have random data.
;with tblA as (
select name,data,
row_number() over(partition by data order by newid()) rn
from A
)
select name,data
from tblA
where rn = 1
Couuld be you need
select * from my_table a
inner join my_table b on a.data !=b.data
where a.data in ( SELECT data FROM my_table ORDER BY RAND() LIMIT 1);
For your code
SELECT *
FROM [dbo].[ComputerState] as a
INNER JOIN [dbo].[ComputerState] as b ON a.ServiceName != b.ServiceName
WHERE a.ServiceName IN (
SELECT top 1 [ServiceName] FROM [dbo].[ComputerState]
);
If the question is really this simple, you can use an aggregate such as MAX() or MIN() to grab one row for each different DATA:
SELECT MAX(NAME), DATA
FROM TABLE_A
GROUP BY DATA
Of course, if any other variables are introduced to the requirements, this may no longer work.
;WITH cteA AS (
SELECT
name
,data
,ROW_NUMBER() OVER (PARTITION BY data ORDER BY NEWID()) as DataRowNumber
,ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY NEWID()) as RandomRowNumber
FROM
A
)
SELECT *
FROM
cteA
WHERe
DataRowNumber = 1
AND RandomRowNumber <= 2
This Expands on #AlexKudryashev 's answer a little.
;with tblA as (
select name,data,
row_number() over(partition by data order by newid()) rn
from A
)
select name,data
from tblA
where rn = 1
The only issue with what he had Is that the number of Rows where rn = 1 will be depended on the COUNT(DISTINCT data) so it could lead to more than 2 results. To fix one could add a SELECT TOP 2 clause but it might not be fully random as results at that point as it will be dependent on the ordinal results of how SQL optimizes the query which is likely to be consistent. To get truly random add a second random row number and limit the results to the top 2 of those.

Dense_Rank with Case statement is not giving Rank with Date

I have an issue while using Dense_Rank with CASE Statement. Below is the
sample table screenshot
So my requirement is two provide a Rank to every employee based on Emp_Dep_id
Req 1-->If Emp_Dep_id is same give same rank
Req 2-->If Emp_Dep_id is null then give same rank only when Emp_Joining_Date and Emp_Country is same
Below is the code to give rank
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
from Employee Emp with (nolock)
Below is Output-->
So,I am facing two issues-
Why Rank is skipping if same ranks is there ex- after second rank why sixth rank is coming next
I want to give rank basis of Emp_Joining_Date Currently it is behaving like firstly it is assigning rank if Emp_Dep_Id is not null after that it is continuing for Emp_Dep_Id is null.
I want to get the rank based on latest Emp_Joining_Date means joining date with 2016 with null should come first
Thanks Guys for your valuable response,I fixed my issue by doing this way
1. Step 1
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Joining_Date,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
into #Emp_Output_Tbl
from Employee Emp
Select * from #Emp_Output_Tbl order by Emp_Joining_date desc
--Step 2
Select distinct rnk,Emp_Joining_date into #Emp_New_Tbl from #Emp_Output_Tbl order by Emp_Joining_date desc
Select * from #Emp_New_Tbl order by Emp_Joining_Date desc
Select * from #Emp_Output_Tbl order by Emp_Joining_Date desc
--Step 3
Select * from #Emp_Output_Tbl where rnk in(
Select TOP 5 rnk from #Emp_New_Tbl
)
order by Emp_Joining_Date desc
**Output as per expectation**
I hope this is going to help
I think you want this logic:
You want a single dense_rank(). The trick is to get the logic into the order by clause.
I think this is what you want:
Select dense_rank() over (order by Emp.Emp_Dep_Id,
(case when Emp.Emp_Dep_Id IS NULL then Emp.Emp_Joining_Date end) desc,
(case when Emp.Emp_Dep_Id IS NULL then Emp.Emp_Country end) desc
)
The Dense rank is working as per the result set & the number of records in it. Please refer the link given
https://msdn.microsoft.com/en-IN/library/ms173825.aspx?
use the below query to know the result better. Also try to use order by after the SQL statement to order it properly.
Select
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country),
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc),
case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
from Employee Emp with (nolock)
Could you please use the below query whether your requirement is met or not. I believe the dense range will operate as your requirement. You don't need to add a extra case statement. Only thing you need to specify is the order of the column. It automatically handle as you required.
Select
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,Emp.Emp_Joining_Date desc,Emp.Emp_Country) as rnk ,*
from Employee Emp with (nolock)
Req 1-->If Emp_Dep_id is same give same rank
Req 2-->If Emp_Dep_id is null then give same rank only when
Emp_Joining_Date and Emp_Country is same
Thanks Guys for your valuable response,I fixed my issue by doing this way
1. Step 1
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Joining_Date,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
into #Emp_Output_Tbl
from Employee Emp
Select * from #Emp_Output_Tbl order by Emp_Joining_date desc
--Step 2
Select distinct rnk,Emp_Joining_date into #Emp_New_Tbl from #Emp_Output_Tbl order by Emp_Joining_date desc
Select * from #Emp_New_Tbl order by Emp_Joining_Date desc
Select * from #Emp_Output_Tbl order by Emp_Joining_Date desc
--Step 3
Select * from #Emp_Output_Tbl where rnk in(
Select TOP 5 rnk from #Emp_New_Tbl
)
order by Emp_Joining_Date desc
**Output as per expectation**

Tricky SQL SELECT Statement

I have a performance issue when selecting data in my project.
There is a table with 3 columns: "id","time" and "group"
The ids are just unique ids as usual.
The time is the creation date of the entry.
The group is there to cummulate certain entries together.
So the table data may look like this:
ID | TIME | GROUP
------------------------
1 | 20090805 | A
2 | 20090804 | A
3 | 20090804 | B
4 | 20090805 | B
5 | 20090803 | A
6 | 20090802 | B
...and so on.
The task is now to select the "current" entries (their ids) in each group for a given date. That is, for each group find the most recent entry for a given date.
Following preconditions apply:
I do not know the different groups in advance - there may be many different ones changing over time
The selection date may lie "in between" the dates of the entries in the table. Then I have to find the closest one in each group. That is, TIME is less than the selection date but the maximum of those to which this rule applies in a group.
What I currently do is a multi-step process which I would like to change into single SELECT statement:
SELECT DISTINCT group FROM table to find the available groups
For each group found in 1), SELECT * FROM table WHERE time<selectionDate AND group=loop ORDER BY time DESC
Take the first row of each result found in 2)
Obviously this is not optimal.
So I would be very happy if some more experienced SQL expert could help me to find a solution to put these steps in a single statement.
Thank you!
The following will work on SQL Server 2005+ and Oracle 9i+:
WITH groups AS (
SELECT t.group,
MAX(t.time) 'maxtime'
FROM TABLE t
GROUP BY t.group)
SELECT t.id,
t.time,
t.group
FROM TABLE t
JOIN groups g ON g.group = t.group AND g.maxtime = t.time
Any database should support:
SELECT t.id,
t.time,
t.group
FROM TABLE t
JOIN (SELECT t.group,
MAX(t.time) 'maxtime'
FROM TABLE t
GROUP BY t.group) g ON g.group = t.group AND g.maxtime = t.time
Here's how I would do it in SQL Server:
SELECT * FROM table WHERE id in
(SELECT top 1 id FROM table WHERE time<selectionDate GROUP BY [group] ORDER BY [time])
The solution will vary by database server, since the syntax for TOP queries varies. Basically you are looking for a "top n per group" query, so you can Google that if you want.
Here is a solution in SQL Server. The following will return the top 10 players who hit the most home runs per year since 1990. The key is to calculate the "Home Run Rank" of each player for each year.
select
HRRanks.*
from
(
Select
b.yearID, b.PlayerID, sum(b.Hr) as TotalHR,
rank() over (partition by b.yearID order by sum(b.hr) desc) as HR_Rank
from
Batting b
where
b.yearID > 1990
group by
b.yearID, b.playerID
)
HRRanks
where
HRRanks.HR_Rank <= 10
Here is a solution in Oracle (Top Salespeople per Department)
SELECT deptno, avg_sal
FROM(
SELECT deptno, AVG(sal) avg_sal
GROUP BY deptno
ORDER BY AVG(sal) DESC
)
WHERE ROWNUM <= 10;
Or using analytic functions:
SELECT deptno, avg_sal
FROM (
SELECT deptno, avg_sal, RANK() OVER (ORDER BY sal DESC) rank
FROM
(
SELECT deptno, AVG(sal) avg_sal
FROM emp
GROUP BY deptno
)
)
WHERE rank <= 10;
Or same again, but using DENSE_RANK() instead of RANK()
select * from TABLE where (GROUP, TIME) in (
select GROUP, max(TIME) from things
where TIME >= 20090804
group by GROUP
)
Tested with MySQL (but I had to change the table and column names because they are keywords).
SELECT *
FROM TABB T1
QUALIFY ROW_NUMBER() OVER ( PARTITION BY GROUPP,TIMEE order by id desc )=1