SQL query: Last but one rank for user - sql

My table structure looks like this:
create table rankings (
id IDENTITY NOT NULL,
user_id INT NOT NULL,
game_poule_id INT NOT NULL,
rank INT NOT NULL,
insertDate DATETIME NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (game_poule_id) REFERENCES game_poules(id) ON DELETE CASCADE
);
All old rankings of users per game are saved in this table. Now I want to have the last but one rank in the table for all users in a gamepoule.
Has someone an idea how to achive this? Thanks

You need to self join the table to get the records you require.
For this answer I created your table without the foreign keys as they are not required to get it to work.
CREATE TABLE Rankings (
id int IDENTITY NOT NULL,
user_id INT NOT NULL,
game_poule_id INT NOT NULL,
rank INT NOT NULL,
insertDate DATETIME NOT NULL
);
Insert some sample data. Without more information I cannot simulate any better than this.
INSERT Rankings(user_id,game_poule_id,rank,insertDate)
VALUES(1, 100, 3, CURRENT_TIMESTAMP-2)
INSERT Rankings(user_id,game_poule_id,rank,insertDate)
VALUES(1, 100, 2, CURRENT_TIMESTAMP-1)
INSERT Rankings(user_id,game_poule_id,rank,insertDate)
VALUES(1, 101, 6, CURRENT_TIMESTAMP)
INSERT Rankings(user_id,game_poule_id,rank,insertDate)
VALUES(2, 100, 5, CURRENT_TIMESTAMP-2)
INSERT Rankings(user_id,game_poule_id,rank,insertDate)
VALUES(2, 100, 1, CURRENT_TIMESTAMP-1)
INSERT Rankings(user_id,game_poule_id,rank,insertDate)
VALUES(2, 101, 2, CURRENT_TIMESTAMP)
Query for the last but one rank
SELECT Rankings.game_poule_id, Rankings.user_id, rank, MAX(Rankings.insertDate)
FROM Rankings INNER JOIN
(SELECT game_poule_id, user_id, MAX(insertDate) max_insert_date
FROM rankings
GROUP BY game_poule_id, user_id) Max_Ranking_Date
ON Rankings.user_id = Max_Ranking_Date.user_id
AND Rankings.insertDate < Max_Ranking_Date.max_insert_date
AND Rankings.game_poule_id = Max_Ranking_Date.game_poule_id
GROUP BY Rankings.game_poule_id, Rankings.user_id, rank
PLEASE NOTE!
As you can see from the results you will not get a ranking for a game that only has one row per user. But since you are asking for the "last but one" that only makes sense for games with multiple entries.
EDIT:
I've just realised the query I have provided will not return one row per user per game. Anyone want to fix it? I have to get on with some work :)

Another possible (not very nice) solution
SELECT
*
FROM
rankings r
WHERE
FK_gamePoule = 0 AND
r.insertDate = COALESCE(
(SELECT
r2.insertDate
FROM
rankings r2
WHERE
r.FK_user = r2.FK_user ORDER BY r2.insertDate DESC
LIMIT 1
OFFSET 1), '2048-12-31 23:59:59')

Related

How can I return the top N most recently inserted records from a table? [duplicate]

I have created table like below:
create table EmployeeDetails
(
id int,
name varchar(50),
designation varchar(50),
ReportingTo int
)
And inserted rows like this:
insert into EmployeeDetails values(1, 'A', 'Developer', 3)
insert into EmployeeDetails values(5, 'E', 'CEO', 5)
insert into EmployeeDetails values(2, 'B', 'Developer', 3)
insert into EmployeeDetails values(3, 'C', 'Manager', 4)
insert into EmployeeDetails values(4, 'D', 'S.Manager', 5)
My question is: how can I identify the last inserted row in the table?
Used queries:
select IDENT_CURRENT('EmployeeDetails')
Select SCOPE_IDENTITY()
But I still didn't find the answer.
Replies are always welcome
You have a fundamental misunderstanding of tables in relational databases. They represent unordered sets. So, there is no "last row" in a table. The information on the ordering of rows has to be included when you create the table and/or load data into it.
In SQL Server, the simplest method is an identity column. That is why many tables are defined as:
create table EmployeeDetails (
id int identity primary key,
name varchar(50),
designation varchar(50),
ReportingTo int
);
You can also add default insertion time columns:
create table EmployeeDetails (
id int identity primary key,
name varchar(50),
designation varchar(50),
ReportingTo int,
CreatedAt datetime default gettime()
);
However, you can have multiple rows with the same datetime value.
You also can use rowversion like below,if you add a column
CREATE TABLE MyTest (myKey int
,myValue int, RV rowversion);
GO
INSERT INTO MyTest (myKey, myValue) VALUES (1, 0);
GO
INSERT INTO MyTest (myKey, myValue) VALUES (2, 0);
select * from mytest order by rv desc
Note:
This always give the last row inserted/Updated
References:
rowversion
Add fields of type datetime with name CreateDate and UpdateDate in your table, when you insert record in your table set their value for getdate()
After that you can run queries:
Select top 10 * from YourTable Order By CreateDate DESC
Or for last updated
Select top 10 * from YourTable Order By UpdateDate DESC
Please find my answer as below. Hope this may help you.
Add one more column to store record creation date/time as below.
create table EmployeeDetails
(
id int,
name varchar(50),
designation varchar(50),
ReportingTo int,
CreatedOn datetime
)
After table creation and inserting records write/execute query (here inner query is used) as below
select
*
from EmployeeDetails
where CreatedOn = (select max(CreatedOn) from EmployeeDetails )

How to reuse value of insert statment

I'm trying to write a SQL statement such that I'm reusing a return value of an insert statement as a value for following insert statement but I have no clue where to start. For example, this is my table schema (didn't write everything).
job {
job_id INT IDENTITY (1, 1) PRIMARY KEY,
start_date,
end_date,
target hours,
worker_id
FOREIGN KEY (worker_id) REFERENCES dbo.worker (worker_id)
}
worker {
worker_id INT IDENTITY (1, 1) PRIMARY KEY,
name
}
And I want to do something like this but I don't think this is the right way to do it
INSERT INTO dbo.job
VALUES(GetDate(), NULL, 8, (
INSERT INTO dbo.worker
OUTPUT
Inserted.worker_id
VALUES ('my_name')
))
Thanks in advance,
best way for your matter is
DECLARE #worker_idtbl TABLE (worker_id INT);
INSERT INTO dbo.worker OUTPUT Inserted.worker_id INTO #worker_idtbl(worker_id) VALUES ('my_name');
INSERT INTO dbo.job VALUES(GetDate(), NULL, 8, (select top 1 worker_id from #worker_idtbl) );

Insert statement Oracle

Here is my table
create table reservations (
ResID int NOT NULL,
GuestID int,
HotelID int,
Check_in DATE,
Check_out DATE,
RoomType varchar2(15),
Price Dec(8,2),
PRIMARY KEY (ResID),
CONSTRAINT FK_GuestIDX FOREIGN KEY(GuestID) REFERENCES Guests(GuestID),
CONSTRAINT FK_HotelID FOREIGN KEY(HotelID) REFERENCES Hotel(HotelID)
);
Table was created with no problems. Now Im trying to populate the table
Here is my insert statement
insert into reservations (1, 1, 2, '17-DEC-2018', '21-DEC-2018', 'Suite', 87.03);
and here is the error that I'm getting
ORA-00928: missing SELECT keyword
What could be the cause for this?
Or missing values:
insert into reservations
values (1, 1, 2, '17-DEC-2018', '21-DEC-2018', 'Suite', 87.03);
That said, I would recommend writing this as:
insert into reservations (ResID, GuestID, HotelID, Check_in, Check_out DATE, RoomType, Price)
values (1, 1, 2, DATE '2018-12-17', DATE '2018-12-21', 'Suite', 87.03);
Note:
List the columns after the insert. This can really prevent hard-to-debug errors.
This uses the DATE keyword to introduce a date constant.
You can also write this using SELECT:
insert into reservations (ResID, GuestID, HotelID, Check_in, Check_out DATE, RoomType, Price)
select 1, 1, 2, DATE '2018-12-17', DATE '2018-12-21', 'Suite', 87.03
from dual;

Need Help Writing SQL Query in Postgresql

I've been trying to write this query but can't seem to get it right for some reason or another..
What I need to do is:
Change the status of a question to 'closed' if there has not been an update associated with this question inserted into the qUpdateTable in the last 24 hours.
I only want it to be closed if a staff member has replied to it at least once.
You can determine if a staff member or a user has replied to the question by checking the qUpdateTable and seeing if a the StaffID field is empty or has a value for that particular tickets updates. If there is a staffID then it has been updated by a staff member, however if it does not then the qUpdate was done by a user.
Essentialy the way this works is a user posts a question by inserting into the Question table, and replies are made by inserting into the qUpdate table and linked to the original question using the foreign key - "QuestionID".
The tables:
CREATE TABLE Staff
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL
);
CREATE TABLE Customer
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL,
Email VARCHAR(40) NOT NULL
);
CREATE TABLE Product
(
ID INTEGER NOT NULL PRIMARY KEY,
Name TEXT NOT NULL
);
CREATE TABLE Question
(
ID INTEGER NOT NULL PRIMARY KEY,
Problem VARCHAR(1000),
Status VARCHAR(20) NOT NULL DEFAULT 'open',
Priority INTEGER NOT NULL,
LoggedTime TIMESTAMP NOT NULL,
CustomerID INTEGER NOT NULL,
ProductID INTEGER NOT NULL,
FOREIGN KEY (ProductID) REFERENCES Product(ID),
FOREIGN KEY (CustomerID) REFERENCES Customer(ID),
CHECK (Status IN ('open','closed') AND Priority IN (1,2,3))
);
CREATE TABLE qUpdate
(
ID INTEGER NOT NULL PRIMARY KEY,
Message VARCHAR(1000) NOT NULL,
UpdateTime TIMESTAMP NOT NULL,
QuestionID INTEGER NOT NULL,
StaffID INTEGER,
FOREIGN KEY (StaffID) REFERENCES Staff(ID),
FOREIGN KEY (QuestionID) REFERENCES Question(ID)
);
Some sample inserts:
INSERT INTO Customer (ID, Name, Email) VALUES (1, 'testname1', 'testemail1');
INSERT INTO Customer (ID, Name, Email) VALUES (2, 'testname2', 'testemail2');
INSERT INTO Staff (ID, Name) VALUES (1, 'Don Keigh');
INSERT INTO Product (ID, Name) VALUES (1, 'Xbox');
INSERT INTO Question (ID, Problem, Status, Priority, LoggedTime, CustomerID, ProductID)
VALUES (1, 'testproblem1', 'open', 3, '2012-04-14 09:30', 2, 1);
INSERT INTO Question (ID, Problem, Status, Priority, LoggedTime, CustomerID, ProductID)
VALUES (2, 'testproblem2', 'open', 3, '2012-04-14 09:30', 2, 1);
INSERT INTO qUpdate (ID, Message, UpdateTime, StaffID, QuestionID) VALUES (2, 'testmessage1','2012-07-12 14:27', 1, 1);
INSERT INTO qUpdate (ID, Message, UpdateTime, QuestionID) VALUES (3, 'testmessage1','2012-06-18 19:42', 2);
What I've done so far (which obviously doesn't work)
UPDATE Question
SET Status = 'closed'
WHERE EXISTS
(SELECT qUpdate.QuestionID
MAX(qUpdate.UpdateTime - Now() = INTERVAL '1 day') FROM qUpdate
LEFT JOIN Question ON qUpdate.QuestionID = Question.ID
WHERE qUpdate.StaffID IS NOT NULL);
I realise my explanation may be a bit confusing so if you need more info post and I'll reply ASAP
UPDATE Question
SET Status = 'closed'
where
-- this clause asserts there's at least one staff answer
exists (
select null from qUpdate
where qUpdate.QuestionID = Question.ID
and StaffID is not null
)
-- this clause asserts there's been no update in the last 24 hours
and not exists (
select null from qUpdate
where qUpdate.QuestionID = Question.ID
and qUpdate.UpdateTime > (now() - interval '24 hours')
)
and Status = 'open';
You'll almost certainly want an index on qUpdate(QuestionID) or possibly qUpdate(QuestionID,UpdateTime) or qUpdate(QuestionID,StaffID) to get good performance on the subselects in the exists() tests.

Insert values or ignore row if a constraint fails

In SQL Server 2008, is there a way to insert rows while omitting those rows that cause a foreign key constraint to fail?
E.g. I have an insert statement similar to this:
insert into tblFoo(id, name, parent_id, desc) values
(1, 'a', 1, null),
(2, 'c', 3, 'blah'),
....;
parent_id is a fk to another table. How can I then get sql server to skip rows on which the fk column is invalid?
Update I would like to get this to work automatically, without first having to filter out those rows that violates the fk constraint. The reason for that is because the insert statements are generated by a program so it is not known beforehand which foreign keys exist on each table.
Is a weird situation you got there but you can insert the values to a temporary table and then select only the values with a valid FK.
something like:
declare #tempTable table (
id int,
name nvarchar(50) ,
parent_id int ,
[desc] nvarchar(50)
)
insert into #tempTable values
(1, 'a', 1, null),
(2, 'c', 3, 'blah')
insert into tblFoo(id, name, parent_id, [desc])
select tempTable.* from #tempTable as tempTable
inner join parent_id on parent_id.id = tempTable.parent_id
One way would be to use an Instead Of trigger on Inserts and Updates. You could then evaluate each row being updated before the actual write to the DB takes place. I'm generally not a huge fan of triggers, but you seem to have an unusual requirement here.