Top and Bottom in the same query? - sql

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.

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%'

Ordering the results of Inner join

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.

MS Access TRIMMEAN how to

I need to perform TREAMMEAN in Access, which does not have this function.
In a table I have many Employees, each has many records.
I need to TRIMMEAN Values for each Employee separately.
Following queries perform TOP 10 percent for all records:
qry_data_TOP10_ASC
qry_data_TOP10_DESC
unionqry_TOP10_ASCandDESC
qry_data_ALL_minus_union_qry
After that, I can use Avg (Average).
But I don't know how to do it for each employee.
Visualization:
Note:
This question is edited to simplify problem.
You don't really give information in your pseudo code about your data fields but using your example that DOES have basic field information I can suggest the following should work as you described
It assumes field1 is your unique record ID - but you make no mention of which fields are keys
SELECT AVG(qry_data.field2) FROM qry_data WHERE qry_data.field1 NOT IN
(SELECT * FROM
(SELECT TOP 10 PERCENT qry_data.field1, qry_data.field2
FROM qry_data
ORDER BY qry_data.field2 ASC)
UNION
(SELECT TOP 10 PERCENT qry_data.field1, qry_data.field2
FROM qry_data
ORDER BY qry_data.field2 DESC)
)
This should give you what you want, the two sub-queries should correlate the TOP 10s (ascending and descending) for every employee. The two NOT INs should then remove those from the Table1 records and then you group the Employees and Average the Scores.
SELECT Table1.Employee, AVG(Table1.Score) AS AvgScore
FROM Table1
WHERE ID NOT IN
(
SELECT TOP 10 ID
FROM Table1 a
WHERE a.Employee = Table1.Employee
ORDER BY Score ASC, Employee, ID
)
AND ID NOT IN
(
SELECT TOP 10 ID
FROM Table1 b
WHERE b.Employee = Table1.Employee
ORDER BY Score DESC, Employee, ID
)
GROUP BY Table1.Employee;

Select random x of top y records

I need to select 5 records randomly out of the top 100 records sorted by amount.
This can be broken into 2 queries but I do not know how to combine them without creating a function (which I'm thinking might be less efficient.
Query 1: SELECT TOP 100 from Cars order by price desc
Query 2: SELECT TOP 5 * FROM (Query1) ORDER BY NEWID()
I'm just not sure how to combine these, whether using an inner joing or just an inner select?
My first reaction was to try this which does not work:
SELECT TOP 5 * FROM (SELECT TOP 100 * FROM Cars order by Price desc) ORDER BY NEWID()
You need to name your subquery:
SELECT TOP 5 * FROM (SELECT TOP 100 * FROM Cars order by Price desc) [A] ORDER BY NEWID()

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