SQL Max on Group By - sql

I'm stuck at a very simple t-sql query. Please help !
Following is my result set :
Percentage---FirstName---SessionId
34-----------ABC---------222
67-----------ABC---------333
11-----------ABC---------444
83-----------XYZ---------555
23-----------XYZ---------666
64-----------XYZ---------777
From above, I want records of each user with maximum percentage e.g.
Percentage---FirstName---SessionId
67-----------ABC---------333
83-----------XYZ---------555
I can't seem to do that and I'm in hurry. Please help at the earliest. Any help would be greatly appreciated.
Thanks.

SELECT MAX(Percentage) AS Percentage,
FirstName
FROM mytbl
GROUP BY FirstName
Or if you need session id:
SELECT mytbl.*
FROM mytbl
INNER JOIN (SELECT MAX(Percentage) AS Percentage,
FirstName
FROM mytbl
GROUP BY FirstName) x ON x.Percentage = mytbl.Percentage
AND x.FirstName = mytbl.FirstName

If you need to deal with ties, then you might want to use the windowing functions (assuming you're on SQL Server 2005 or later):
SELECT
*
FROM
(SELECT mytbl.*,RANK() OVER (PARTITION BY FirstName ORDER BY Percentage desc) as rn) t
WHERE
t.rn = 1
So if there are two rows with the same percentage, they'll both be returned. If you only want one result, then you can add additional "tie-breaker" columns to the ORDER BY clause, or switch from RANK() to ROW_NUMBER().

Related

DBIx::Class select from a subquery

I'm trying to write the following SQL query using DBIx::Class:
SELECT * FROM (SELECT username, DENSE_RANK () OVER (ORDER BY COUNT(username) DESC) FROM myTable GROUP BY username) AS topUsers WHERE username='myUsername';
myTable consists of only two rows, one being the username and the other one representing an achievement of the user. The subquery is supposed to assign a rank to each user and the main query selects the rank of myUser.
I'm having some issues in translating this to DBIx::Class so I would appreciate some help!
Thank you in advance!
You should define your query as view. See here
After that you can:
$schema->resultset( 'YourView' )->search
This will generate SQL:
select * from ( <your query here> )

SQL: find most common values for specific members in column 1

I have the following SQL related question:
Let us assume I have the following simple data table:
I would like to identify the most common street address and place it in column 3:
I think this should be fairly straight-forward using COUNT? Not quite sure how to go about it though. Any help is greatly appreciated
Regards
This is a very long method that I just wrote. It only lists the most frequent address. You have to get these values and insert them into the table. See if it works for you:
select * from
(select d.company, count(d.address) as final, c.maxcount,d.address
from dbo.test d inner join
(select a.company,max(a.add_count) as maxcount from
(select company,address,count(address) as add_count from dbo.test group by company,address)a
group by a.company) c
on (d.company = c.company)
group by d.company,c.maxcount,d.address)e
where e.maxcount=e.final
Here is a query in standard SQL. It first counts records per company and address, then ranks them per company giving the most often occurring address rank #1. Then it only keeps those best ranked address records, joins with the table again and shows the results.
select
mytable.company,
mytable.address,
ranked.address as most_common_address
from mytable
join
(
select
company,
address,
row_number() over (partition by company oder by cnt desc) as rn
from
(
select
company,
address,
count(*) over (partition by company, address) as cnt
from mytable
) counted
) ranked on ranked.rn = 1
and ranked.company = mytable.company
and ranked.address = mytable.address;
This select statement will give you the most frequent occurrence. Let us call this A.
SELECT `value`,
COUNT(`value`) AS `value_occurrence`
FROM `my_table`
GROUP BY `value`
ORDER BY `value_occurrence` DESC
LIMIT 1;
To INSERT this into your table,
INSERT INTO db (col1, col2, col3) VALUES (val1, val2, A)
Note that you want that whole select statment for A!
You don't mention your DBMS. Here is a solution for Oracle.
select
company,
address,
(
select stats_mode(address)
from mytable this_company_only
where this_company_only.company = mytable.company
) as most_common_address
from mytable;
This looks a bit clumsy, because STATS_MODE is only available as an aggregate function, not as an analytic window function.

How to add serial number over a randomly generated resultset in SQL Server?

I am stuck in a situation where I have to add a 'Serial No.' over a resultset in SQL.
The scenario is like below:
MyTable is like,
Now, after using the following query,
SELECT *
FROM MyTable
ORDER BY CHECKSUM(NEWID())
I am getting output like this,
Now, I want an output like this,
Using ROW_NUMBER() OVER (ORDER BY ID) AS SlNo gives me the serial number just same as my ID, which is not at serially ordered (and this is logically correct too).
Please help me out with some solutions. I am using SQL Server 2008 R2.
Pardon me for my mistakes. Thank you.
In SQL Server, the following will work - it will order by the first column of the result. It still provides a random order.
SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS SlNo, *
FROM MyTable ORDER BY 1
You can order by the first column
SELECT CHECKSUM(NEWID()), *
FROM MyTable
ORDER BY 1
Or you can make a subquery so you can order by column name
SELECT * FROM
(
SELECT CHECKSUM(NEWID()) AS SLNo, *
FROM MyTable
) TBL
ORDER BY SLNo

SQL Server - Selecting multiple maximum values matching two separate corresponding cells

I am a beginner in SQL and been trying to obtain all of the maximum values as per the access strength and folder names, with allowing groups to the folders being displayed accordingly. I tried using MAX function but it was returning only 1 maximum result. Thank you in advance for any help or guidance of how t achieve the specified results.
Please see the jpg link attached for the top table and the desired outcome in the bottom table.
John
You can use the RANK function to put your records in an order, then select only the top 1:
WITH CTE AS
( SELECT Folder_name,
[User],
Access,
Group_Allowing,
[Rank] = RANK() OVER(PARTITION BY Folder_Name, [User] ORDER BY Access DESC)
FROM T
)
SELECT Folder_name,
[User],
Access,
Group_Allowing
FROM CTE
WHERE [Rank] = 1;
The PARTITION BY clause is similar to GROUP BY, and the ranking will start again at 0 for each group. The order by simply then indicates what order to do the ranking in.
You'll want to put your query using MAX() into a subquery and JOIN to it:
SELECT a.*
FROM YourTable a
JOIN (SELECT Folder_Name, [User], MAX(Access) Max_Access
FROM YourTable
GROUP BY Folder_Name, [User]
)b
ON a.Folder_Name = b.Folder_Name
AND a.[User] = b.[User]
AND a.Access = b.Max_Access
You can use "ORDER BY Access DESC" and "LIMIT [number of entries you want]" or if you want to just get the highest number you can use "GROUP BY Access" and then use max() to display all the entries you want

Selecting Nth Record in an SQL Query

I have an SQL Query that i'm running but I only want to select a specific row. For example lets say my query was:
Select * from Comments
Lets say this returns 10 rows, I only want to select the 8th record returned by this query. I know I can do:
Select Top 5 * from Comments
To get the top 5 records of that query but I only want to select a certain record, is there anything I can put into this query to do that (similar to top).
Thanks
jack
This is a classic interview question.
In Ms SQL 2005+ you can use the ROW_NUMBER() keyword and have the Predicate ROW_NUMBER = n
USE AdventureWorks;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
FROM Sales.SalesOrderHeader
)
SELECT *
FROM OrderedOrders
WHERE RowNumber = 5;
In SQL2000 you could do something like
SELECT Top 1 *FROM
[tblApplications]
where [ApplicationID] In
(
SELECT TOP 5 [ApplicationID]
FROM [dbo].[tblApplications]
order by applicationId Desc
)
How about
SELECT TOP 1 * FROM
(SELECT TOP 8 * FROM Comments ORDER BY foo ASC)
ORDER BY foo DESC
First, you should say which RDBMS you're using.
Second, you should give careful thought to what it is you're trying to accomplish. Relational Databases are set-based. In general, the order of elements in a set does not matter. You'll want to ask why it matters in this case, then see if there's a better way to embed the concept of order into the query itself.
For instance, in SQL Server 2005 (and other RDBMS), you can use the ROW_NUMBER function to assign a sequential number to each row returned, based on the criteria you specify. You could then select rows based on the row number. Example from Books Online:
USE AdventureWorks;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
FROM Sales.SalesOrderHeader
)
SELECT *
FROM OrderedOrders
WHERE RowNumber BETWEEN 50 AND 60;
SELECT * FROM comments WHERE ...conditions... LIMIT 1 OFFSET 8
OFFSET is a good thing for MySQL
For SQL Server 2005:
select rank() OVER (ORDER BY c.subject, c.date) as rank, c.subject, c.date
from comments c
where rank = 8
Well, in T-SQL (the dialect for SQL Server) you can do the following:
SELECT TOP 1 *
FROM (SELECT TOP 8 *
FROM Table
ORDER
BY SortField)
ORDER
BY SortField DESC
This way you get the 8th record.
I have read the question & your comments on you would want next 3 blog comments etc.
How is your tables structured?
Assume that you have blog post Id & comment Id is generated in ascending order for each blog post, you could do a SELECT based on the current Id.
e.g. if the blogpostId = 101, you get the top 3 comments order by posted Id. Now lets say, you want to get the next 3 comments - you could do a SELECT WHERE commentId between the last comment id shown TO the comment id - 3
But all that depends on how your tables are defined.
In SQL 2000 where you do not have ROW_NUMBER() function you could use a work-around like this:
SELECT CommentsTableFieldList, IDENTITY(INT, 1,1) as seqNo
INTO #SeqComments
FROM Comments
SELECT * FROM #SeqComments
WHERE seqNo = 8
select top 1 *
from TableName
where ColumnName1 in
(
select top nth ColumnName1
from TableName
order by ColumnName1 desc
)
order by ColumnName1 desc
From the SELECT reference, use the LIMIT keyword:
SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15
SELECT * FROM tbl LIMIT 5; # Retrieve first 5 rows
Note: this is for MySQL, other SQL engines may have a different keyword.
Select from tablename limit nthrow,1;
try This
Let us assume , We want select 5th row of WC_Video Table
And
Select * from (Select Row_Number() over (Order by Uploadedon) as 'rownumber',* from Wc_Video )as Temp where rownumber=5