SQL Select exact rows - sql

I got some tables and now I want to determine the current rank of each customer.
I got a log table that holds all the Information when a customer got a "point" then I created a view that counts the "points" for every customer. Now I'm trying to create another view that matches the customers points with the current Rank he has. Furthermore I got a "rank" table that holds the name of the rank and the min points you need to have to reach that rank. My Problem is now that when I do
SELECT r.neededVisits, r.name, av.customerId
FROM Rank r, amountOfVisits av
WHERE av.amount >= r.neededVisits
I get something like this:
[Table Output]
The left column "besuche" holds the value that is needed for that rank i.e. for the rank "gast" you need 0 visits. For the rank "Stammgast" you need 25 visits.
So I get every rank that a customer ever passed. But I just want to get the last rank for each customer
Is there any way I can do this?
Desired Result would be something like this:
[Deisred Result]
The Table that holds the ranks
[Rank Table]
[Rank Table Values]
The table that holds the counted visits for each user
[Amount of visits for each user]
[Amount of visits Table values]

I assume you are looking for something like
SELECT r.neededVisits, r.name, av.customerId
FROM Rank r, amountOfVisits av
WHERE av.amount >= r.neededVisits
AND NOT EXISTS (SELECT * FROM rank r2 WHERE r2.neededVisits < r.neededVisits AND av.amount >= r2.neededVisits)
This uses your current logic, but the final condition removes the in between ranks.
As pointed out in the comments, you should probably try to rewrite with an inner join which would be more like
SELECT r.neededVisits, r.name, av.customerId
FROM Rank r INNER JOIN amountOfVisits av
ON av.amount >= r.neededVisits
WHERE NOT EXISTS (SELECT * FROM rank r2 WHERE r2.neededVisits < r.neededVisits AND av.amount >= r2.neededVisits)
I think the above is pretty readable, but a more modern method (depending on your DBMS) would be to use window functions. Something like
SELECT neededVisits, name, customerId
FROM
(
SELECT r.neededVisits, r.name, av.customerId, RANK() OVER (PARTITION BY av.customerId ORDER BY r.neededVisits DESC) tr
FROM Rank r INNER JOIN amountOfVisits av
ON av.amount >= r.neededVisits
) iq
WHERE tr=1
The inner query here calculates a column "tr" that is ordered DESC based on the matching ranks for that customer. The outer query gets the first one.

Related

SQL Getting Top 2 Results for each individual column value

I have a table 'Cashup_Till' that records all data on what a particular till has recorded in a venue for a given day, each venue has multiple tills all with a designated number 'Till_No'. I need to get the previous 2 days entries for each till number. For each till Individually I can do this...
SELECT TOP 2 T.* FROM CashUp_Till T
WHERE T.Till_No = (Enter Till Number Here)
ORDER BY T.Till_Id DESC
Some venues have 20-30 tills so Ideally I need to do all the tills in one call. I can pass in a user defined table type of till numbers, then select them in a subquery, but that's as far as my SQL knowledge takes me, does anyone have a solution?
Here is one way:
SELECT T.*
FROM (SELECT T.*,
ROW_NUMBER() OVER (PARTITION BY Till_No ORDER BY Till_Id DESC) as seqnum
FROM CashUp_Till T
) T
WHERE seqnum <= 2;
This assumes that there is one record per day, which I believe is suggested by the question.
If you have a separate table of tills, then:
select ct.*
from t cross apply
(select top 2 ct.*
from cashup_till ct
where ct.till_no = t.till_no
order by till_id desc
) ct;

SQL Server : get latest date from 2 tables

I have two tables P and G and want to write a query that will get the latest date from table G and will not pull in duplicate client IDs:
Table P
Table G
I want to get this result from the query:
So far I have joined the tables, but unable get the result intended.
Any help would be appreciated.
Not sure how your tables are related other than your column ClientID, but you would want to join the two tables on those columns:
select p.clientid,
max(g.created_on) latest_created_on,
max(p.info) as info
from tableP p
left join tableG g on p.ClientID = g.ClientID
group by p.clientid;
SQL Fiddle Demo
You can use OVER PARTITION to take the record with the most recent date for each ClientID.
In this case, I would write:
SELECT g.ClientID,
g.created_on,
g.INFO
FROM (
SELECT ClientID
created_on,
INFO,
row_number() OVER ( PARTITION BY ClientID ORDER BY created_on DESC) AS RowNum
FROM Table_G
) AS g
WHERE g.RowNum = 1
The subquery creates a table with all the columns you want, and the row_number() function assigns each record a row_number. PARTITION BY says what to group by, and ORDER BY says how to sort within that partition.
In this case, you want the record with the most recent date for each ClientID. We group by ClientID, sort by date to assign row numbers, and then in the main query, we select only the first row in each group, using WHERE g.RowNum = 1
This is a guide for PostreSQL, but it's helped me understand OVER PARTITION.

SQL Finding maximum value without top command

Let's say I have a bases with a table:
-courses (key: name [ofthecourse], other attributes: year in which the course takes place)
I want to complete a query looking for an answer to the question:
On which year of study there is a maximum number of courses?
Normally, the query would be:
SELECT TOP 1 STUDYEAR
FROM COURSES
GROUP BY STUDYEAR
ORDER BY COUNT(CNO) DESC;
But my question is, which query could complete this without using the TOP 1 phrase?
You can use an inner query to get the maximum count. The only difference is though that it can return more than one record if they have the same count.
SELECT STUDYEAR
FROM COURSES
GROUP BY STUDYEAR
HAVING COUNT(CNO) = (SELECT MAX(CNOCount) FROM
(SELECT COUNT(CNO) CNOCount
FROM COURSES
GROUP BY STUDYEAR) X)
Another version with only one inner query:
SELECT STUDYEAR
FROM
(SELECT STUDYEAR, ROW_NUMBER() OVER (ORDER BY COUNT(CNO) DESC) RowNumber
FROM COURSES
GROUP BY STUDYEAR) X
WHERE RowNumber = 1

Get records with the newest date in Oracle

I need to find the emails of the last person that performed an action over a post. The database structure is a little bit complicated because of several reasons not important for the case.
SELECT u.address
FROM text t
JOIN post p ON (p.pid=t.pid)
JOIN node n ON (n.nid=p.nid)
JOIN user u ON (t.login=u.login)
WHERE n.nid='123456'
AND p.created IN (
SELECT max(p.created)
FROM text t
JOIN post p ON (p.pid=t.pid)
JOIN node n ON (n.nid=p.nid)
WHERE n.nid='123456');
I would like to know if there is a way to do use the max function or any other way to get the latest date without having to make a subquery (that is almost the same as the main query).
Thank you very much
You can use a window function (aka "analytical" function) to calculate the max date.
Then you can select all rows where the created date equals the max. date.
select address
from (
SELECT u.address,
p.created,
max(p.created) over () as max_date
FROM text t
JOIN post p ON (p.pid=t.pid)
JOIN node n ON (n.nid=p.nid)
JOIN user u ON (t.login=u.login)
WHERE n.nid='123456'
) t
where created = max_date;
The over() clause is empty as you didn't use a GROUP BY in your question. But if you need e.g. the max date per address then you could use
max(p.created) over (partition by t.adress) as max_date
The partition by works like a group by
You can also extend that query to work for more than one n.id. In that you you have to include it in the partition:
max(p.created) over (partition by n.id, ....) as max_date
Btw: if n.id is a numeric column you should not compare it to a string literal. '123456' is a string, 123456 is a number
SELECT address
FROM (
SELECT u.address,
row_number() OVER (PARTITION BY n.nid ORDER BY p.created DESC) AS rn
FROM text t JOIN post p ON (p.pid=t.pid)
JOIN node n ON (n.nid=p.nid)
JOIN user u ON (t.login=u.login)
WHERE n.nid='123456'
)
WHERE rn = 1;
The ROW_NUMBER function numbers the rows in descending order of p.created with PARTITION BY n.nid making separate partitions for row numbers of separate n.nids.

One row of data for a max date only - transact SQL

I am trying to select the max dates on a field with other tables, to only give me one distinct row for the max date and not other rows with other dates. the code i have for max is
SELECT DISTINCT
Cust.CustId,
LastDate=(Select Max(Convert(Date,TreatmentFieldHstry.TreatmentDateTime))
FROM TreatmentFieldHstry
WHERE Cust.CustSer = Course.CustSer
AND Course.CourseSer = Session.CourseSer
AND Session.SessionSer = TreatmentFieldHstry.SessionSer)
This gives multiple rows depending on how many dates - i just want one for the max - can anyone help with this?
Thanks
You didn't specify exactly what database and version you're using - but if you're on SQL Server 2005 or newer, you can use something like this (a CTE with the ROW_NUMBER ranking function) - I've simplified it a bit, since I don't know what those other tables are that you have in your select, that don't ever show up in any of the SELECT column lists.....
;WITH TopData AS
(
SELECT c.CustId, t.TreatmentDateTime,
ROW_NUMBER() OVER(PARTITION BY c.CustId ORDER BY t.TreatmentDateTime DESC) AS 'RowNum'
FROM
dbo.TreatmentFieldHstry t
INNER JOIN
dbo.Customer c ON c.CustId = t.CustId -- or whatever JOIN condition you have
WHERE
c.CustSer = Course.CustSer
)
SELECT
*
FROM
TopData
WHERE
RowNum = 1
Basically, the CTE (Common Table Expression) partitions your data by CustId and order by TreatmentDateTime (descending - newest first) - and numbers every entry with a consecutive number - for each "partition" (e.g. for each new value of CustId). With this, the newest entry for each customer has RowNum = 1 which is what I use to select it from that CTE.