ORDER BY in GROUP BY clause - sql

I have a query
Select
(SELECT id FROM xyz M WHEREM.ID=G.ID AND ROWNUM=1 ) TOTAL_X,
count(*) from mno G where col1='M' group by col2
Now from subquery i have to fetch ramdom id for this I am doing
Select
(SELECT id FROM xyz M WHEREM.ID=G.ID AND ROWNUM=1 order by dbms_random.value ) TOTAL_X,
count(*) from mno G where col1='M' group by col2
But , oracle is showing an error
"Missing right parenthesis".
what is wrong with the query and how can i wrtie this query to get random Id.
Please help.

Even if what you did was legal, it would not give you the result you want. The ROWNUM filter would be applied before the ORDER BY, so you would just be sorting one row.
You need something like this. I am not sure if this exact code will work given the correlated subquery, but the basic point is that you need to have a subquery that contains the ORDER BY without the ROWNUM filter, then apply the ROWNUM filter one level up.
WITH subq AS (
SELECT id FROM xyz M WHERE M.ID=G.ID order by dbms_random.value
)
SELECT (SELECT id FROM subq WHERE rownum = 1) total_x,
count(*)
from mno g where col1='M' group by col2

You can't use order by in a subselect. It wouldn't matter too, because the row numbering is applied first, so you cannot influence it by using order by,
[edit]
Tried a solution. Don't got Oracle here, so you'll have to read between the typos.
In this case, I generate a single random value, get the count of records in xyz per mno.id, and generate a sequence for those records per mno.id.
Then, a level higher, I filter only those records whose index match with the random value.
This should give you a random id from xyz that matches the id in mno.
select
x.mnoId,
x.TOTAL_X
from
(SELECT
g.id as mnoId,
m.id as TOTAL_X,
count(*) over (partition by g.id) as MCOUNT,
dense_rank() over (partition by g.id) as MINDEX,
r.RandomValue
from
mno g
inner join xyz m on m.id = g.id
cross join (select dbms_random.value as RandomValue from dual) r
where
g.col1 = 'M'
) x
where
x.MINDEX = 1 + trunc(x.MCOUNT * x.RandomValue)

The only difference between your two lines are that you order_by in the one that fails, right?
It so happens that order_by doesn't fit inside a nested select.
You could do an order_by inside a where clause that contains a select, though.
Edit: #StevenV is right.

If you're trying to do what I suspect, this should work
Select A.Id, Count(*)
From MNO A
Join (Select ID From XYZ M Where M.ID=G.ID And Rownum=1 Order By Dbms_Random.Value ) B On (B.ID = A.ID)
GROUP BY A.ID

Related

How to find Max value in a column in SQL Server 2012

I want to find the max value in a column
ID CName Tot_Val PName
--------------------------------
1 1 100 P1
2 1 10 P2
3 2 50 P2
4 2 80 P1
Above is my table structure. I just want to find the max total value only from the table. In that four row ID 1 and 2 have same value in CName but total val and PName has different values. What I am expecting is have to find the max value in ID 1 and 2
Expected result:
ID CName Tot_Val PName
--------------------------------
1 1 100 P1
4 2 80 P1
I need result same as like mention above
select Max(Tot_Val), CName
from table1
where PName in ('P1', 'P2')
group by CName
This is query I have tried but my problem is that I am not able to bring PName in this table. If I add PName in the select list means it will showing the rows doubled e.g. Result is 100 rows but when I add PName in selected list and group by list it showing 600 rows. That is the problem.
Can someone please help me to resolve this.
One possible option is to use a subquery. Give each row a number within each CName group ordered by Tot_Val. Then select the rows with a row number equal to one.
select x.*
from ( select mt.ID,
mt.CName,
mt.Tot_Val,
mt.PName,
row_number() over(partition by mt.CName order by mt.Tot_Val desc) as No
from MyTable mt ) x
where x.No = 1;
An alternative would be to use a common table expression (CTE) instead of a subquery to isolate the first result set.
with x as
(
select mt.ID,
mt.CName,
mt.Tot_Val,
mt.PName,
row_number() over(partition by mt.CName order by mt.Tot_Val desc) as No
from MyTable mt
)
select x.*
from x
where x.No = 1;
See both solutions in action in this fiddle.
You can search top-n-per-group for this kind of a query.
There are two common ways to do it. The most efficient method depends on your indexes and data distribution and whether you already have another table with the list of all CName values.
Using ROW_NUMBER
WITH
CTE
AS
(
SELECT
ID, CName, Tot_Val, PName,
ROW_NUMBER() OVER (PARTITION BY CName ORDER BY Tot_Val DESC) AS rn
FROM table1
)
SELECT
ID, CName, Tot_Val, PName
FROM CTE
WHERE rn=1
;
Using CROSS APPLY
WITH
CTE
AS
(
SELECT CName
FROM table1
GROUP BY CName
)
SELECT
A.ID
,A.CName
,A.Tot_Val
,A.PName
FROM
CTE
CROSS APPLY
(
SELECT TOP(1)
table1.ID
,table1.CName
,table1.Tot_Val
,table1.PName
FROM table1
WHERE
table1.CName = CTE.CName
ORDER BY
table1.Tot_Val DESC
) AS A
;
See a very detailed answer on dba.se Retrieving n rows per group
, or here Get top 1 row of each group
.
CROSS APPLY might be as fast as a correlated subquery, but this often has very good performance (and better than ROW_NUMBER():
select t.*
from t
where t.tot_val = (select max(t2.tot_val)
from t t2
where t2.cname = t.cname
);
Note: The performance depends on having an index on (cname, tot_val).

SQL Sort by group size

I want to make a select query which groups rows based on a given column and then sorts by size of such groups.
Let's say we have this sample data:
id type
1 c
2 b
3 b
4 a
5 c
6 b
I want to obtain the following by grouping and sorting the column 'type' in a descending way:
id type
2 b
3 b
6 b
1 c
5 c
4 a
As of now I am only able to get the count of each group but that is not exactly what I need:
SELECT *, COUNT(type) AS typecount
FROM sampletable
GROUP BY type
ORDER BY typecount DESC, type ASC
id type count
2 b 3
1 c 2
4 a 1
Can anybody please give me a hand with this query?
Edit:
Made 'b' the biggest group to avoid coming to the same solution by using only SORT BY
You can't use a column alias in your GROUP BY; just repeat the expression:
SELECT type, COUNT(type) AS count
FROM sampletable
GROUP BY type
ORDER BY COUNT(*) DESC, type ASC
Note that I changed the SELECT clause - you can't use * in your SELECT either since expressions in the SELECT need to either be in the GROUP BY clause or an aggregation.
It may not be the best way, but it will give you what you want.
You work out the totals for each group and then join that "virtual" table to your original table by the determined counts.
SELECT *
FROM sampletable s1
INNER JOIN (SELECT count(type) AS iCount,type
FROM sampletable
GROUP BY type) s2 ON s2.type = s1.type
ORDER BY s2.iCount DESC, s1.type ASC
http://sqlfiddle.com/#!9/f6b0c4/6/0
You can't perform GROUP BY operation on COLUMN ALIAS.
The reason why you can't use ALIAS on the GROUP BY clause that is created on the same level of the SELECT statement is because the GROUP BY is executed before the SELECT clause in which the ALIAS is created.
This is the SQL Order of Operation:
FROM clause
WHERE clause
GROUP BY clause
HAVING clause
SELECT clause
ORDER BY clause
Try following query:
SELECT type, COUNT(type) AS count
FROM sampletable
GROUP BY type
ORDER BY COUNT(*) DESC, type ASC;
EDIT:-
SELECT id, type
FROM sampletable
ORDER BY type DESC, id ASC;

Most frequent attribute for a subject not returning correct number of distinct subjects

I was trying to write an SQL SELECT to return the most frequent TIPO_ASSEPSIA for each distinct EPISODIO in my sql server.
Well from what I've seen the SELECT technically works (ex:in an EPISODIO with 3 rows, if two of the TIPO_ASSEPSIA are the same, it will choose that attribute as the most frequent) but when I went to check the number of rows I get, I get 3822 rows. This can't possibly be right since if I count the number of distinct EPISODIO I get 3897, so there are several dozens of rows missing and I don't know why. Any ideas?
The code I am using is the following one
SELECT DISTINCT
F1.EPISODIO,
F1.TIPO_ASSEPSIA
FROM DWS_DM F1
WHERE
F1.TIPO_ASSEPSIA =
( SELECT t.TIPO_ASSEPSIA from
(
SELECT TOP 1 TIPO_ASSEPSIA , (COUNT(*)) AS freq
FROM DWS_DM F2
WHERE F2. EPISODIO = F1.EPISODIO
GROUP BY F2.TIPO_ASSEPSIA
ORDER BY count(TIPO_ASSEPSIA) DESC)t
)
SELECT DISTINCT
F1.EPISODIO,
x.TIPO_ASSEPSIA
FROM DWS_DM F1
OUTER APPLY
( SELECT TOP 1 r.TIPO_ASSEPSIA , (COUNT(*)) AS freq
FROM DWS_DM r
WHERE r.EPISODIO = F1.EPISODIO
GROUP BY TIPO_ASSEPSIA
ORDER BY count(TIPO_ASSEPSIA) DESC
) x
Why are you referring back to your F1 alias? I think you want to do this:
SELECT DISTINCT
F1.EPISODIO,
F1.TIPO_ASSEPSIA
FROM DWS_DM F1
WHERE
F1.TIPO_ASSEPSIA =
( SELECT t.TIPO_ASSEPSIA from
(
SELECT TOP 1 TIPO_ASSEPSIA , (COUNT(*)) AS freq
FROM DWS_DM
GROUP BY TIPO_ASSEPSIA
ORDER BY count(TIPO_ASSEPSIA) DESC)t
)

SQL query to select distinct row with minimum value

I want an SQL statement to get the row with a minimum value.
Consider this table:
id game point
1 x 5
1 z 4
2 y 6
3 x 2
3 y 5
3 z 8
How do I select the ids that have the minimum value in the point column, grouped by game? Like the following:
id game point
1 z 4
2 y 5
3 x 2
Use:
SELECT tbl.*
FROM TableName tbl
INNER JOIN
(
SELECT Id, MIN(Point) MinPoint
FROM TableName
GROUP BY Id
) tbl1
ON tbl1.id = tbl.id
WHERE tbl1.MinPoint = tbl.Point
This is another way of doing the same thing, which would allow you to do interesting things like select the top 5 winning games, etc.
SELECT *
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Point) as RowNum, *
FROM Table
) X
WHERE RowNum = 1
You can now correctly get the actual row that was identified as the one with the lowest score and you can modify the ordering function to use multiple criteria, such as "Show me the earliest game which had the smallest score", etc.
This will work
select * from table
where (id,point) IN (select id,min(point) from table group by id);
As this is tagged with sql only, the following is using ANSI SQL and a window function:
select id, game, point
from (
select id, game, point,
row_number() over (partition by game order by point) as rn
from games
) t
where rn = 1;
Ken Clark's answer didn't work in my case. It might not work in yours either. If not, try this:
SELECT *
from table T
INNER JOIN
(
select id, MIN(point) MinPoint
from table T
group by AccountId
) NewT on T.id = NewT.id and T.point = NewT.MinPoint
ORDER BY game desc
SELECT DISTINCT
FIRST_VALUE(ID) OVER (Partition by Game ORDER BY Point) AS ID,
Game,
FIRST_VALUE(Point) OVER (Partition by Game ORDER BY Point) AS Point
FROM #T
SELECT * from room
INNER JOIN
(
select DISTINCT hotelNo, MIN(price) MinPrice
from room
Group by hotelNo
) NewT
on room.hotelNo = NewT.hotelNo and room.price = NewT.MinPrice;
This alternative approach uses SQL Server's OUTER APPLY clause. This way, it
creates the distinct list of games, and
fetches and outputs the record with the lowest point number for that game.
The OUTER APPLY clause can be imagined as a LEFT JOIN, but with the advantage that you can use values of the main query as parameters in the subquery (here: game).
SELECT colMinPointID
FROM (
SELECT game
FROM table
GROUP BY game
) As rstOuter
OUTER APPLY (
SELECT TOP 1 id As colMinPointID
FROM table As rstInner
WHERE rstInner.game = rstOuter.game
ORDER BY points
) AS rstMinPoints
This is portable - at least between ORACLE and PostgreSQL:
select t.* from table t
where not exists(select 1 from table ti where ti.attr > t.attr);
Most of the answers use an inner query. I am wondering why the following isn't suggested.
select
*
from
table
order by
point
fetch next 1 row only // ... or the appropriate syntax for the particular DB
This query is very simple to write with JPAQueryFactory (a Java Query DSL class).
return new JPAQueryFactory(manager).
selectFrom(QTable.table).
setLockMode(LockModeType.OPTIMISTIC).
orderBy(QTable.table.point.asc()).
fetchFirst();
Try:
select id, game, min(point) from t
group by id

Oracle SQL order by in subquery problems!

I am trying to run a subquery in Oracle SQL and it will not let me order the subquery columns. Ordering the subquery is important as Oracle seems to choose at will which of the returned columns to return to the main query.
select ps.id, ps.created_date, pst.last_updated, pst.from_state, pst.to_state,
(select last_updated from mwcrm.process_state_transition subpst
where subpst.last_updated > pst.last_updated
and subpst.process_state = ps.id
and rownum = 1) as next_response
from mwcrm.process_state ps, mwcrm.process_state_transition pst
where ps.created_date > sysdate - 1/24
and ps.id=pst.process_state
order by ps.id asc
Really should be:
select ps.id, ps.created_date, pst.last_updated, pst.from_state, pst.to_state,
(select last_updated from mwcrm.process_state_transition subpst
where subpst.last_updated > pst.last_updated
and subpst.process_state = ps.id
and rownum = 1
order by subpst.last_updated asc) as next_response
from mwcrm.process_state ps, mwcrm.process_state_transition pst
where ps.created_date > sysdate - 1/24
and ps.id=pst.process_state
order by ps.id asc
Both dcw and Dems have provided appropriate alternative queries. I just wanted to toss in an explanation of why your query isn't behaving the way you expected it to.
If you have a query that includes a ROWNUM and an ORDER BY, Oracle applies the ROWNUM first and then the ORDER BY. So the query
SELECT *
FROM emp
WHERE rownum <= 5
ORDER BY empno
gets an arbitrary 5 rows from the EMP table and sorts them-- almost certainly not what was intended. If you want to get the "first N" rows using ROWNUM, you would need to nest the query. This query
SELECT *
FROM (SELECT *
FROM emp
ORDER BY empno)
WHERE rownum <= 5
sorts the rows in the EMP table and returns the first 5.
Actually "ordering" only makes sense on the outermost query -- if you order in a subquery, the outer query is permitted to scramble the results at will, so the subquery ordering does essentially nothing.
It looks like you just want to get the minimum last_updated that is greater than pst.last_updated -- its easier when you look at it as the minimum (an aggregate), rather than a first row (which brings about other problems, like what if there are two rows tied for next_response?)
Give this a shot. Fair warning, been a few years since I've had Oracle in front of me, and I'm not used to the subquery-as-a-column syntax; if this blows up I'll make a version with it in the from clause.
select
ps.id, ps.created_date, pst.last_updated, pst.from_state, pst.to_state,
( select min(last_updated)
from mwcrm.process_state_transition subpst
where subpst.last_updated > pst.last_updated
and subpst.process_state = ps.id) as next_response
from <the rest>
I've experienced this myself and you have to use ROW_NUMBER(), and an extra level of subquery, instead of rownum...
Just showing the new subquery, something like...
(
SELECT
last_updated
FROM
(
select
last_updated,
ROW_NUMBER() OVER (ORDER BY last_updated ASC) row_id
from
mwcrm.process_state_transition subpst
where
subpst.last_updated > pst.last_updated
and subpst.process_state = ps.id
)
as ordered_results
WHERE
row_id = 1
)
as next_response
An alternative would be to use MIN instead...
(
select
MIN(last_updated)
from
mwcrm.process_state_transition subpst
where
subpst.last_updated > pst.last_updated
and subpst.process_state = ps.id
)
as next_response
The confirmed answer is plain wrong.
Consider a subquery that generates a unique row index number.
For example ROWNUM in Oracle.
You need the subquery to create the unique record number for paging purposes (see below).
Consider the following example query:
SELECT T0.*, T1.* FROM T0 LEFT JOIN T1 ON T0.Id = T1.Id
JOIN
(
SELECT DISTINCT T0.*, ROWNUM FROM T0 LEFT JOIN T1 ON T0.Id = T1.Id
WHERE (filter...)
)
WHERE (filter...) AND (ROWNUM > 10 AND ROWNUM < 20)
ORDER BY T1.Name DESC
The inner query is the exact same query but DISTINCT on T0.
You can't put the ROWNUM on the outer query since the LEFT JOIN(s) could generate many more results.
If you could order the inner query (T1.Name DESC) the generated ROWNUM in the inner query would match.
Since you cannot use an ORDER BY in the subquery the numbers wont match and will be useless.
Thank god for ROW_NUMBER OVER (ORDER BY ...) which fixes this issue.
Although not supported by all DB engines.
One of the two methods, LIMIT (does not require ORDER) and the ROW_NUMBER() OVER will cover most DB engines.
But still if you don't have one of these options, for example the ROWNUM is your only option then a ORDER BY on the subquery is a must!