Ordering the results of Inner join - sql

I currently have the following query:
select X.sellerID, x.Category, y.Award_Year, Y.Award
from Y
inner join
X on Y.Seller_ID=X.sellerID
Which give me this results:
How can I write a query to get Top 3 seller and the amount of awards recieved in 2017. Following is expected result:

I guess you want the top 3 sellers by descending order (i.e. most awards in top) and you could do a flashy pivot thing with filtering on year to get a more general query, but I suspect something like this one would be more suitable at this level..
The top 3 clause will differ between different DB Engines
MSSQL : TOP (nr) directly after SELECT
Oracle: FETCH FIRST nr ROWS ONLY last in statement
mysql: LIMIT nr last in statement
etc....
FOR MSSQL it would be something like
SELECT TOP (3) sellerID, Category, Award_Year, COUNT(Award_Year) Awards
from Y y
inner join
X x on y.Seller_ID=x.sellerID
WHERE Award_Year = 2017
GROUP BY x.sellerID, x.Category, y.Award_Year
Order By COUNT(Award_Year) DESC

Try this:
SELECT TOP(3) A.Id, A.category, count(A.Award) AS 'Award in 2017'
FROM (
SELECT X.sellerID AS ID, x.Category, y.Award_Year, Y.Award
FROM Y
INNER JOIN X on Y.Seller_ID=X.sellerID
) A
GROUP BY A.Id, A.category
ORDER BY count(A.Award) DESC
Depending on your actual DBMS version, you may need to amend it.

Related

Show SQL result row in context of others

I have a query showing how a particular entry ranks:
select launch_rank, partner_info from summary WHERE "partner_info" LIKE "%Example%"
However it's only useful in context when it is ranked together with:
How do I show the entry with 10 competitors on either side of it? Without resorting to static queries like WHERE launch_rank > 140 and launch_rank < 200?
Assuming you have one row that you are comparing to and the ranks are actually different on each row, you can use:
with onerow as (
select launch_rank
from summary
where partner_info LIKE '%Example%'
)
select s.*
from (select s.*
from summary s
where s.launch_rank <= (select launch_rank from onerow)
order by s.launch_rank desc
limit 11
) s
union all
select s.*
from (select s.*
from summary s
where s.launch_rank > (select launch_rank from onerow)
order by s.launch_rank asc
limit 10
) s
Just join the table to itself:
select A.launch_rank, A.partner_info from summary A INNER JOIN summary B
ON A.launch_rank>=B.launch_rank-10 AND A.launch_rank<=B.launch_rank+10
WHERE B."partner_info" LIKE '%Example%'

Top and Bottom in the same query?

I need to make a ssrs-report that shows the best and the worst customer depending on how much they've spent. In my report i would like to represent the gap between the Top 1 and Bottom customer, within one chart. My problem is that it's impossible for me to get these values within the same dataset/query.
This is my results from a query(see code below). I would like to, with maybe union all or something, get the same result from only one query. Or is there a easier way with e.g. Visual Studio to represent these values. Top N, Bottom N filters perhaps? If so please show me a way or "best practice" cuz i haven't figured it out yet. thx.
Code:
SELECT DISTINCT TOP 1
dimcustomer.FirstName ,
SUM(FactInternetSales.OrderQuantity * UnitPrice)
FROM DimCustomer
INNER JOIN FactInternetSales ON FactInternetSales.CustomerKey = DimCustomer.CustomerKey
GROUP BY FirstName
ORDER BY SUM(FactInternetSales.OrderQuantity * UnitPrice) DESC
SELECT DISTINCT TOP 1
dimcustomer.FirstName ,
SUM(FactInternetSales.SalesAmount)
FROM DimCustomer
INNER JOIN FactInternetSales ON FactInternetSales.CustomerKey = DimCustomer.CustomerKey
GROUP BY FirstName
ORDER BY SUM(FactInternetSales.SalesAmount) DESC
Two result sets:
FirstName | SalesAmount
Morgan 145044,5816
------------------------
FirstName | SalesAmount
Dave 3.99
The union operator doesn't like the order by clause so you can restructure slightly
with CustomersOrders as
(
select dimcustomer.FirstName, sum(FactInternetSales.OrderQuantityUnitPrice) Total
from DimCustomer
inner join FactInternetSales on FactInternetSales.CustomerKey = DimCustomer.CustomerKey
group by FirstName
)
select *
from
(
select top 1 *
from CustomersOrders
order by Total desc
) a
union all
select *
from
(
select top 1 *
from CustomersOrders
order by Total
) b
You can UNION these queries, and add another column "CustomerType" to queries with values - TopCustomer & BottomCustomer correspondingly to distinguish the customer type.

Adding count in select query

I am trying to find a query that would give me a count of another table in the query. The problem is that I have no idea what to set where in the count part to. As it is now it will just give back a count of all the values in that table.
Select
ID as Num,
(select Count(*) from TASK where ID=ID(Also tried Num)) as Total
from ORDER
The goal is to have a result that reads like
Num Total
_________________
1 13
2 5
3 22
You need table aliases. So I think you want:
Select ID as Num,
(select Count(*) from TASK t where t.ID = o.ID) as Total
from ORDER o;
By the way, ORDER is a terrible name for a table because it is a reserved work in SQL.
You can do it as a sub query or a join (or an OVER statement.)
I think the join is clearest when you are first learning SQL
Select
ID as Num, count(TASK.ID) AS Total
from ORDER
left join TASK ON ORDER.ID=TASK.ID
GROUP BY ORDER.ID

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

Compare SQL groups against eachother

How can one filter a grouped resultset for only those groups that meet some criterion compared against the other groups? For example, only those groups that have the maximum number of constituent records?
I had thought that a subquery as follows should do the trick:
SELECT * FROM (
SELECT *, COUNT(*) AS Records
FROM T
GROUP BY X
) t HAVING Records = MAX(Records);
However the addition of the final HAVING clause results in an empty recordset... what's going on?
In MySQL (Which I assume you are using since you have posted SELECT *, COUNT(*) FROM T GROUP BY X Which would fail in all RDBMS that I know of). You can use:
SELECT T.*
FROM T
INNER JOIN
( SELECT X, COUNT(*) AS Records
FROM T
GROUP BY X
ORDER BY Records DESC
LIMIT 1
) T2
ON T2.X = T.X
This has been tested in MySQL and removes the implicit grouping/aggregation.
If you can use windowed functions and one of TOP/LIMIT with Ties or Common Table expressions it becomes even shorter:
Windowed function + CTE: (MS SQL-Server & PostgreSQL Tested)
WITH CTE AS
( SELECT *, COUNT(*) OVER(PARTITION BY X) AS Records
FROM T
)
SELECT *
FROM CTE
WHERE Records = (SELECT MAX(Records) FROM CTE)
Windowed Function with TOP (MS SQL-Server Tested)
SELECT TOP 1 WITH TIES *
FROM ( SELECT *, COUNT(*) OVER(PARTITION BY X) [Records]
FROM T
)
ORDER BY Records DESC
Lastly, I have never used oracle so apolgies for not adding a solution that works on oracle...
EDIT
My Solution for MySQL did not take into account ties, and my suggestion for a solution to this kind of steps on the toes of what you have said you want to avoid (duplicate subqueries) so I am not sure I can help after all, however just in case it is preferable here is a version that will work as required on your fiddle:
SELECT T.*
FROM T
INNER JOIN
( SELECT X
FROM T
GROUP BY X
HAVING COUNT(*) =
( SELECT COUNT(*) AS Records
FROM T
GROUP BY X
ORDER BY Records DESC
LIMIT 1
)
) T2
ON T2.X = T.X
For the exact question you give, one way to look at it is that you want the group of records where there is no other group that has more records. So if you say
SELECT taxid, COUNT(*) as howMany
GROUP by taxid
You get all counties and their counts
Then you can treat that expressions as a table by making it a subquery, and give it an alias. Below I assign two "copies" of the query the names X and Y and ask for taxids that don't have any more in one table. If there are two with the same number I'd get two or more. Different databases have proprietary syntax, notably TOP and LIMIT, that make this kind of query simpler, easier to understand.
SELECT taxid FROM
(select taxid, count(*) as HowMany from flats
GROUP by taxid) as X
WHERE NOT EXISTS
(
SELECT * from
(
SELECT taxid, count(*) as HowMany FROM
flats
GROUP by taxid
) AS Y
WHERE Y.howmany > X.howmany
)
Try this:
SELECT * FROM (
SELECT *, MAX(Records) as max_records FROM (
SELECT *, COUNT(*) AS Records
FROM T
GROUP BY X
) t
) WHERE Records = max_records
I'm sorry that I can't test the validity of this query right now.