Select random x of top y records - sql

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()

Related

Select Minimum value of column A where Column B=1

I have this table AVERAGE
I want to
select * from table where VhrNum=MIN(VhrNum) and EmptyOrNot=1
I've Tried this query but it's not working
select *
from Average
where Itmnum='1'
and VhrNum = (
select MIN(VhrNum)
from (
select *
from Average
where EmptyOrNot = '1'
)
)
In my case it's should select the third row
Why not just take the top 1 row and order by by the column?
SELECT TOP 1 *
FROM [table]
WHERE EmptyOrNot=1
ORDER BY VhrNum
Use TOP 1 with ORDER BY
select Top 1 * from table where EmptyOrNot=1
Order by VhrNum ASC
If you more than one record with min VhrNum value and you want all the tie records then use TOP 1 WITH TIES
select Top 1 With Ties * from table where EmptyOrNot=1
Order by VhrNum ASC
You can try like this
SELECT MIN(VhrNum) FROM table_name where EmptyOrNot=1;
Aggregate functions need a GROUP BY - or you could ORDER BY and select the first.
Aggregate won't let you do a SELECT *, which isn't really good practice anyway, and aggregate makes it clearer that you're actually trying to get the MIN of this. TOP 1/ORDER BY would let you do a SELECT *, but may be less immediately clear that all you're really trying to get is the MIN(VhrNum).
Aggregate:
SELECT MIN(VhrNum)
FROM Average
WHERE EmptyOrNot = 1
GROUP BY EmptyOrNot
Top:
SELECT TOP(1) VhrNum, *
FROM Average
WHERE EmptyOrNot = 1
ORDER BY VhrNum ASC
You can try this:
select top 1 * from table where VhrNum= (select min(VhrNum) from table);

Select SQL event log with 5 previous rows

I'm trying to track down a specific event and I want to list also the 5 previous rows.
I can list all the events this way:
select *
from Payment
where PaymentCardNumber like '%MyEvent%'
but as I want to list also the 5 previous rows I would like to create a query that search for the primary key:
select *
from Payment
where PrimaryKey between (select PrimaryKey -5
from TablePayment
where PaymentCardNumber like '%MyEvent%')
and (select PrimaryKey
from TablePayment
where PaymentCardNumber like '%MyEvent%')
Of course this is not working because instead of giving 2 numbers I'm giving to SQL a list of numbers.
So how to organize a function or loop that could query the 5 previous rows of every event?
Here is one way:
WITH e AS (
SELECT PrimaryKey
FROM Payment
WHERE PaymentCardNumber like '%MyEvent%'
)
SELECT e.PrimaryKey as comparisonPK, p.*
FROM e OUTER APPLY
(SELECT TOP 5 p.*
FROM p
WHERE p.PrimaryKey < e.PrimaryKey
ORDER BY p.PrimaryKey DESC
) p
UNION ALL
SELECT e.PrimaryKey as comparisonPK, p.*
FROM e OUTER APPLY
(SELECT TOP 5 p.*
FROM p
WHERE p.PrimaryKey > e.PrimaryKey
ORDER BY p.PrimaryKey ASC
) p
ORDER BY comparisonPK, PrimaryKey;
The CTE gets the primary key of the events you care about. The OUTER APPLY chooses five rows before and after that row.
select TOP 5 *
from Payment
WHERE PaymentCardNumber like '%MyEvent%'
ORDER BY
PrimaryKey DESC
you can use SELECT TOP and sort descending to get the 5 most recent events matching your where statement but as Gordon appropriately points out you might also get more than 1 event. If you want 5 latest records per event that meet your criteria I would use ROW_NUMBER() function available since SQL-Server 2008.
;WITH cte AS (
select *
,ROW_NUMBER() OVER (PARTITION BY PaymentCardNumber ORDER BY PrimaryKey DESC) as RowNumber
from Payment
WHERE PaymentCardNumber like '%MyEvent%'
)
SELECT *
FROM
cte
WHERE
RowNumber <= 5

Issue with SQL "Order by"

I would like to compose a SQL query that for example selects the top 2 values of a sorted table using ORDER BY with a query below, but I'm getting unexpected results
select top 2 * from (select top 100 percent * from events order by dates desc) a ;
let's say for example, if the values of the table were 1,3,4
using
select top 100 percent * from events order by dates desc;
would give me 4,3,1
but with
select top 2 * from (select top 100 percent * from events order by dates desc) a;
I get 1,3 instead of 4,3
I was wondering why is that the case and am I missing something?
use order by in outer query as well
select top 2 * from (select top 100 percent * from events order by dates desc) a order by dates desc

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.

get random top n rows where n is greater than quantity of rows in table

i'm writing a script that generates random data. i have two tables, one that stores first names, and second that stores surnames.
i want to get e.g. 1000 random pairs of first name and surname. i can achieve it using following code:
with x as (
select top 1000 f.firstName from dbo.firstNames f order by newid()
), xx as (
select x.firstName, row_number() over(order by x.firstName) as nameNo from x
), y as (
select top 1000 s.surName from dbo.surNames s order by newid()
), yy as (
select y.surName, row_number() over(order by y.ulica) as nameNo from y
)
select xx.firstName, yy.surName
from xx inner join yy on (xx.nameNo=yy.nameNo)
...but what if one of my tables contains less than 1000 rows?
i wondered how to get more than n rows from table where n is less than quantity of rows in table/view and you don't mind repeated results.
the only way i could think of is to use temp table and while loop, and fill it with random rows until there is enough rows. But i wonder if it's possible to do it with a single select? i'm currently using sql server 2012 on my PC, but i would appreciate it if i could run it under sql server 2008, too.
You could do the randomization after the cross join:
select top 1000 fn.firstname, sn.surname
from firstnames fn cross join
surnames sn
order by newid();
I'm the first to admit that the problem with this approach is performance, but it does work in theory. And performance is probably fine if the tables have at most a few hundred rows.
If you want 1000 random pairs then 32 from each table should suffice (32*32=1024):
WITH f1 AS (
SELECT TOP 32 firstName FROM dbo.firstName ORDER BY newid()
), s1 AS
SELECT TOP 32 surName FROM dbo.surName ORDER BY newid()
)
SELECT f1.firstName, s1.surName
FROM f1 CROSS JOIN s1;
If that's not random enough then you might try the following:
WITH f1 AS (
SELECT TOP 100 firstName FROM dbo.firstName ORDER BY newid()
), s1 AS
SELECT TOP 100 surName FROM dbo.surName ORDER BY newid()
)
SELECT TOP 1000 f1.firstName, s1.surName
FROM f1 CROSS JOIN s1
ORDER BY newid();
The above would get the 10,000 combinations and select 1,000 of them at random.