Select SQL event log with 5 previous rows - sql

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

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

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;

get ROW NUMBER of random records

For a simple SQL like,
SELECT top 3 MyId FROM MyTable ORDER BY NEWID()
how to add row numbers to them so that the row numbers become 1,2, and 3?
UPDATE:
I thought I can simplify my question as above, but it turns out to be more complicated. So here is a fuller version -- I need to give three random picks (from MyTable) for each person, with pick/row number of 1, 2, and 3, and there is no logical joining between person and picks.
SELECT * FROM Person
LEFT JOIN (
SELECT top 3 MyId FROM MyTable ORDER BY NEWID()
) D ON 1=1
The problem with above SQL are,
Obviously, pick/row number of 1, 2, and 3 should be added
and what is not obvious is that, the above SQL will give each person the same picks, whereas I need to give different person different picks
Here is a working SQL to test it out:
SELECT TOP 15 database_id, create_date, cs.name FROM sys.databases
CROSS apply (
SELECT top 3 Row_number()OVER(ORDER BY (SELECT NULL)) AS RowNo,*
FROM (SELECT top 3 name from sys.all_views ORDER BY NEWID()) T
) cs
So, Please help.
NOTE: This is NOT about MySQL byt T-SQL as their syntax are different, Thus the solution is different as well.
Add Row_number to outer query. Try this
SELECT Row_number()OVER(ORDER BY (SELECT NULL)),*
FROM (SELECT TOP 3 MyId
FROM MyTable
ORDER BY Newid()) a
Logically TOP keyword is processed after Select. After Row Number is generated random 3 records will be pulled. So you should not generate Row Number in original query
Update
It can be achieved through CROSS APPLY. Replace the column names inside cross apply where clause with valid column name from Person table
SELECT *
FROM Person p
CROSS apply (SELECT Row_number()OVER(ORDER BY (SELECT NULL)) rn,*
FROM (SELECT TOP 3 MyId
FROM MyTable
WHERE p.some_col = p.some_col -- Replace it with some column from person table
ORDER BY Newid())a) cs

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