I am working on a game , where i have a table called punishment which have following schema
CREATE TABLE Punishment
(
PunishmentId int identity(1,1) not null ,
PunishmentDay int ,
PunishmentMonth int ,
PunishmentYear int ,
GameId int
)
PunishmentDay ,PunishmentMonth ,PunishmentYear are numbers which can be either zero or null or any number.
GameId can be repeat in this table , means i can get multiple times punishment for the same game.
Now my question is i have to get the punishmentId in which user get the highest punishment.
I have tried following way but not able to get the max record ..
SELECT PunishmentId, DATEADD(DD,PunishmentDay,DATEADD(MM,PunishmentMonth,(DATEADD(YY,PunishmentYear,GETDATE()))))
FROM Punishment
You can use ROW_NUMBER() instead of a correlated subquery to find the max year/month/day. ROW_NUMBER() will allow you assign an incrementing row number based on an order by clause. You can then select only rows where that rownumber = 1. Try something like this:
SELECT * FROM
( SELECT PunishmentId,PunishmentDay,PunishmentMonth,PunishmentYear, DATEADD(DD,PunishmentDay,DATEADD(MM,PunishmentMonth,(DATEADD(YY,PunishmentYear,GETDATE())))) TotalDays, ROW_NUMBER() OVER(PARTITION BY GameId ORDER BY PunishmentYear, PunishmentMonth, PunishmentDay DESC) RowNumber
FROM Punishment
WHERE GameId = #GameId
) OrderedPunishment
WHERE RowNumber = 1
Note: I haven't checked this for syntax, and I based the statement off your statement (pretty much ignored your nested dateadds, maybe there is a better way to do that too). I also only just now noticed your second table name ConvictCases_G... I didn't see that that is supposed to be Punishment.
This should work
SELECT TOP 1 PunishmentId
FROM
(
SELECT TOP 100 PERCENT
PunishmentId ,
SUM(PunishmentDay + PunishmentMonth*30 + PunishmentYear*360) AS MaxPunishment
FROM #p
GROUP BY PunishmentId
ORDER BY SUM(PunishmentDay + PunishmentMonth*30 + PunishmentYear*360) DESC
)
AS X
I have solved this by following sql
SELECT PunishmentId,PunishmentDay,PunishmentMonth,PunishmentYear, DATEADD(DD,PunishmentDay,DATEADD(MM,PunishmentMonth,(DATEADD(YY,PunishmentYear,GETDATE()))))
FROM Punishment
WHERE GameId=#GameId and
DATEADD(DD,PunishmentDay,DATEADD(MM,PunishmentMonth,(DATEADD(YY,PunishmentYear,GETDATE()))))
= (SELECT MAX(DATEADD(DD,PunishmentDay,DATEADD(MM,PunishmentMonth,(DATEADD(YY,PunishmentYear,GETDATE()))))) FROM Punishment where GameId=#GameId)
but still waiting if there any better solution can be done ..
You could also use:
SELECT TOP 1 WITH TIES
PunishmentId,PunishmentDay,PunishmentMonth,PunishmentYear,
DATEADD(DD,PunishmentDay,DATEADD(MM,PunishmentMonth,(DATEADD(YY,PunishmentYear,GETDATE())))) AS PunishmentEndDate
FROM Punishment
WHERE GameId=#GameId
ORDER BY PunishmentEndDate DESC
Related
I have two results with two different dates (a recent one and the previous one) the numbers below are the result 250 being the most recent and 300 being the previous result:
250
300
The code I use is here:
SELECT TOP 2
MY FIELD as bmi
FROM
MY TABLE
ORDER BY
THE DATE FIELD DESC
Within this same code I want to be able to find the difference between those two numbers and for that to appear not the two numbers?
I have tried a few things of skipping N rows etc but now I don't know what I can do?
I think you want something like this:
declare #firstBmiRes int
declare #secondBmiRes int
SET #firstBmiRes = 250 /* insert your query */
SET #secondBmiRes = 300 /* insert your query */
(SELECT SUM(#secondBmiRes - #firstBmiRes))
If you want to continue to use the calculated result. You can obviously store the value into another variable like this:
declare #bmi int
SET #bmi = (SELECT SUM(#secondBmiRes - #firstBmiRes))
SELECT #bmi
2nd Approach:
Since we don't have very much information to work with. you could try something like this... But i'm assuming a lot of your datastructure here.
declare #BmiScore int
declare #firstBmiRes int
declare #secondBmiRes int
SET #firstBmiRes = (SELECT TOP 1 MY_FIELD
FROM MY_TABLE
ORDER BY DATE_FIELD DESC)
SET #secondBmiRes = (SELECT MY_FIELD
FROM MY_TABLE
ORDER BY DATE_FIELD DESC
OFFSET 1 ROW
FETCH NEXT 1 ROW ONLY)
SET #bmiScore = (SELECT SUM(#secondBmiRes - #firstBmiRes))
SELECT #bmiScore
SELECT
MYFIELD - LAG (MYFIELD,1) OVER (ORDER BY MYDATE) AS BMI
FROM
MYTABLE;
ORDER BY MYDATE DESC
Using a LEAD function if you want your code to be a part of new code for some reason:
select TOP 1 (bmi - lead(bmi) over (order by date_field)) as result
from( SELECT TOP 2 my_field as bmi
, date_field
FROM my_table
ORDER BY date_field DESC) A
Here is a DEMO
Or by LAG :
select TOP 1 (lag(my_field) over (order by date_field) - my_field ) as result
FROM my_table
ORDER BY date_field DESC;
You can use LEAD/ LAG if your version of SQL Server supports these functions. If you are on an older version then you can use a windowed function to apply an order to the rows.
Here's your data going into a temporary table variable:
DECLARE #MY_TABLE TABLE (THE_DATE_FIELD DATE, MY_FIELD INT);
INSERT INTO #MY_TABLE SELECT '20200114', 300 UNION ALL SELECT '20200113', 250;
...and here's a query to perform the calculation you needed:
WITH x AS (
SELECT TOP 2
THE_DATE_FIELD,
MY_FIELD AS bmi,
ROW_NUMBER() OVER (ORDER BY THE_DATE_FIELD DESC) AS order_id
FROM
#MY_TABLE)
SELECT
MAX(CASE WHEN order_id = 1 THEN bmi END) - MAX(CASE WHEN order_id = 2 THEN bmi END) AS difference_bmi
FROM
x;
If I peek at the data from the CTE then I see this (and this is why I included the date field, which is redundant, and could otherwise be removed):
THE_DATE_FIELD bmi order_id
2020-01-14 300 1
2020-01-13 250 2
Now it's simply a case of picking the two values, as one has an order_id = 1 and one has an order_id = 2.
I have two select statements which is returning duplicated data. What I'm trying to accomplish is to remove a duplicated leg. But I'm having hard times to get to the second row programmatically.
select i.InvID, i.UID, i.StartDate, i.EndDate, i.Minutes,i.ABID from inv_v i, InvoiceLines_v i2 where
i.Period = '2014/08'
and i.EndDate = i2.EndDate
and i.Minutes = i2.Minutes
and i.Uid <> i2.Uid
and i.abid = i2.abid
order by i.EndDate
This select statement returns the following data.
As you can see it returns duplicate rows where minutes are the same ABID is the same but InvID are different. What I need to do is to remove one of the InvID where the criteria matches. Doesn't matter which one.
The second select statement is returning different data.
select i.InvID, i.UID, i.StartDate, i.EndDate, i.Minutes from InvoiceLines_v i, InvoiceLines_v i2 where
i.Period = '2014/08'
and i.EndDate = i2.EndDate
and i.Uid = i2.Uid
and i.Abid <> i2.Abid
and i.Language <> i2.Language
order by i.startdate desc
In this select statement I want to remove an InvID where UID is the same then select the lowest Mintues. In This case, I would remove the following InvIDs: 2537676 , 2537210
My goal is to remove those rows...
I could accomplish this using cursor grab the InvID and remove it by simple delete statement, but I'm trying to stay away from cursors.
Any suggestions on how I can accomplish this?
You can use exists to delete all duplicates except the one with the highest InvID by deleting those rows where another row exists with the same values but with a higher InvID
delete from inv_v
where exists (
select 1 from inv_v i2
where i2.InvID > inv_v.InvID
and i2.minutes = inv_v.minutes
and i2.EndDate = inv_v.EndDate
and i2.abid = inv_v.abid
and i2.uid <> inv_v.uid -- not sure why <> is used here, copied from question
)
I have faced similar problems regarding duplicate data and some one told me to use partition by and other methods but those were causing performance issues
However , I had a primary key in my table through which I was able to select one row from the duplicate data and then delete it.
For example in the first select statement "minutes" and "ABID" are the criteria to consider duplicacy in data.But "Invid" can be used to distinguish between the duplicate rows.
So you can use below query to remove duplicacy.
delete from inv_i where inv_id in (select max(inv_id) from inv_i group by minutes,abid having count(*) > 1 );
This simple concept was helpful to me. It can be helpful in your case if "Inv_id" is unique.
;WITH CTE AS
(
SELECT InvID
,[UID]
,StartDate
,EndDate
,[Minutes]
,ROW_NUMBER() OVER (PARTITION BY InvID, [UID] ORDER BY [Minutes] ASC) rn
FROM InvoiceLines_v
)
SELECT *
FROM CTE
WHERE rn = 1
Replace the ORIGINAL_TABLE with your table name.
QUERY 1:
WITH DUP_TABLE AS
(
SELECT ROW_NUMBER()
OVER (PARTITION BY minutes, ABID ORDER BY minutes, ABID) As ROW_NO
FROM <ORIGINAL_TABLE>
)
DELETE FROM DUP_TABLE WHERE ROW_NO > 1;
QUERY 2:
WITH DUP_TABLE AS
(
SELECT ROW_NUMBER()
OVER (PARTITION BY UID ORDER BY minutes) As ROW_NO
FROM <ORIGINAL_TABLE>
)
DELETE FROM DUP_TABLE WHERE ROW_NO > 1;
I have a sql query to return IT tickets and their satisfaction scores however because of the way our ticketing system works this sometimes returns 2 rows for a ticket reference - 1 with a "Not Rated" rating and one with the real rating.
My question is, is there a way to get the query to only return a single row for each Ticket Reference and only return the "Not Rated" rating if another rating does not exist. i.e. when there are 2 rows with the same ticket reference and one has a rating of "Excellent" and one has a rating of "Not Rated" that it only returns the row with the "Excellent" rating. Any with only 1 row that is "Not Rated" should be returned.
The query looks like this so far -
SELECT DISTINCT
t.Rating_Date,
t.id AS 'Ticket Reference',
[Rating]
= CASE
When tt.[rating_id] = 20 then 'Poor'
When tt.[rating_id] = 15 then 'Average'
When tt.[rating_id] = 17 then 'Good'
When tt.[rating_id] = 6 then 'Excellent'
else 'Not Rated'
END,
[subject],
[priority],
[status],
uu.name,
[assignee_id],
[Location],
[technology]
FROM
[DB1].[dbo].[table1] t
INNER JOIN [DB1].[dbo].[table2] tt
ON t.id=tt.ticket_id
LEFT JOIN [DB1].[dbo].[table3] uu
ON t.assignee_id=uu.id
WHERE
t.rating_date > '2013-07-01'
AND status = 'closed'
AND location = 'UK'
AND technology <> 'Not Known'
AND group_id = '5678912'
ORDER BY
t-rating_date
I think you can wrap the first query, order it by rating_id (if Not rated value is 0 you should order DESC, if Not rated is the highest you should order ASC) and then select the first record using the TOP function of sql-server.
I suppose that the following logic can be implemented here. Please, be aware of that I am using sample data and in order to implement the solution you should understand the idea.
Let's say we have the following data:
Note: I am using id "0" for "Not Rated" status.
If I have understand your need correctly, the output data should be something like this:
As you can see from the screenshot above, for Tickets with IDs 1 and 4 we have "Not Rated" records, but we are not showing them. The "Not Rated" state is displayed only for Ticket with ID 3.
The solution:
;WITH DataSource AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY [TicketID] ORDER BY [TicketRateID] DESC) AS [RowNumber]
,[TicketID]
,[TicketRateID]
FROM #DataSource
)
SELECT [TicketID]
,[RowNumber]
,[TicketRateID]
FROM DataSource
The SQL statement above is using ROW_NUMBER function in order to create a unique ID for each set of records for given tickets. We are sorting the records with DESC directive in order to be sure the "0"/"Not rated" records will have bigger ID.
The output of previous statement is:
As you can see from the screenshot above, we need to display only this records with RowNumber equal to 1. This is done simple with WHERE clause.
Follows, full working example:
SET NOCOUNT ON
GO
DECLARE #DataSource TABLE
(
[TicketID] TINYINT
,[TicketRateID] TINYINT
)
INSERT INTO #DataSource ([TicketID],[TicketRateID])
VALUES (1,6)
,(1,0)
,(2,20)
,(3,0)
,(4,0)
,(4,15)
;WITH DataSource AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY [TicketID] ORDER BY [TicketRateID] DESC) AS [RowNumber]
,[TicketID]
,[TicketRateID]
FROM #DataSource
)
SELECT [TicketID]
,[RowNumber]
,[TicketRateID]
FROM DataSource
WHERE [RowNumber] = 1
SET NOCOUNT OFF
GO
Consider the following table structure for an imaginary table named score:
player_name |player_lastname |try |score
primary key: (player_name,player_lastname,try)
(dont discuss the table schema, its just an example)
This table holds the scores of all players - every player should be able to play either one OR two times. Now, how could I fetch data about every player's last try only (i.e. first tries should be ignored for those who played more than once)?
An example of what I'm trying to achieve:
player_name,player_lastname,try,score
=====================================
bart, simpson,1,250
lisa,simpson,1,150
lisa,simpson,2,250
homer,simpson,1,300
homer,simpson,2,350
maggi,simpson,1,50
The result should be:
player_name,player_lastname,try,score
=====================================
bart, simpson,1,250
lisa,simpson,2,250
homer,simpson,2,350
maggi,simpson,1,50
One option is to JOIN the table to itself using a subquery with MAX:
select s.*
from score s
join (
select max(try) maxtry, player_name, player_lastname
from score
group by player_name, player_lastname
) s2 on s.player_name = s2.player_name
and s.player_lastname = s2.player_lastname
and s.try = s2.maxtry
SQL Fiddle Demo
Depending on your database, you may be able to take advantage of analytic functions such as ROW_NUMBER() though which would make this easier. Here is a another fiddle to demonstrate.
Since you are using postgresql, then you should be able to use the analytic ROW_NUMBER() function. This should work as well:
select *
from (
select try, player_name, player_lastname, score,
Row_Number() Over (Partition By player_name, player_lastname order by try desc) rn
from score
) s
where rn = 1
BTW -- I'd consider adding a player_id as a primary key.
This will probably have the best performance
select distinct on (player_name, player_lastname)
player_name, player_lastname, try, score
from score
order by 1, 2, 3 desc
A Rank function can solve this:
SELECT player_name,player_lastname,TRY,score
FROM (SELECT player_name,player_lastname,TRY,score,RANK() OVER (PARTITION BY player_name, Player_Lastname ORDER BY TRY DESC)AS try_rank
FROM score
)sub
WHERE try_rank = 1
I'm assuming 'try' is the number that can be 1/2.
Edit, forgot Partition BY
SELECT player_name,player_lastname,try,score
FROM scores sc
WHERE NOT EXISTS (
SELECT *
FROM scores nx
WHERE nx.player_name = sc.player_name
AND nx.player_lastname = sc.player_lastname
AND nx.try > sc.try
);
Try this out:
Sel player_name,
player_lastname,
try,
score
from score where try = 2 or
try = 1 and
(player_name,player_lastname) not in
(sel player_name,player_lastname from score where try=2);
How do I page results in SQL Server 2005?
I tried it in SQL Server 2000, but there was no reliable way to do this. I'm now wondering if SQL Server 2005 has any built in method?
What I mean by paging is, for example, if I list users by their username, I want to be able to only return the first 10 records, then the next 10 records and so on.
Any help would be much appreciated.
You can use the Row_Number() function.
Its used as follows:
SELECT Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
From which it will yield a result set with a RowID field which you can use to page between.
SELECT *
FROM
( SELECT Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
) As RowResults
WHERE RowID Between 5 AND 10
etc
If you're trying to get it in one statement (the total plus the paging). You might need to explore SQL Server support for the partition by clause (windowing functions in ANSI SQL terms). In Oracle the syntax is just like the example above using row_number(), but I have also added a partition by clause to get the total number of rows included with each row returned in the paging (total rows is 1,262):
SELECT rn, total_rows, x.OWNER, x.object_name, x.object_type
FROM (SELECT COUNT (*) OVER (PARTITION BY owner) AS TOTAL_ROWS,
ROW_NUMBER () OVER (ORDER BY 1) AS rn, uo.*
FROM all_objects uo
WHERE owner = 'CSEIS') x
WHERE rn BETWEEN 6 AND 10
Note that I have where owner = 'CSEIS' and my partition by is on owner. So the results are:
RN TOTAL_ROWS OWNER OBJECT_NAME OBJECT_TYPE
6 1262 CSEIS CG$BDS_MODIFICATION_TYPES TRIGGER
7 1262 CSEIS CG$AUS_MODIFICATION_TYPES TRIGGER
8 1262 CSEIS CG$BDR_MODIFICATION_TYPES TRIGGER
9 1262 CSEIS CG$ADS_MODIFICATION_TYPES TRIGGER
10 1262 CSEIS CG$BIS_LANGUAGES TRIGGER
The accepted answer for this doesn't actually work for me...I had to jump through one more hoop to get it to work.
When I tried the answer
SELECT Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
WHERE RowID Between 0 AND 9
it failed, complaining that it didn't know what RowID was.
I had to wrap it in an inner select like this:
SELECT *
FROM
(SELECT
Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
) innerSelect
WHERE RowID Between 0 AND 9
and then it worked.
When I need to do paging, I typically use a temporary table as well. You can use an output parameter to return the total number of records. The case statements in the select allow you to sort the data on specific columns without needing to resort to dynamic SQL.
--Declaration--
--Variables
#StartIndex INT,
#PageSize INT,
#SortColumn VARCHAR(50),
#SortDirection CHAR(3),
#Results INT OUTPUT
--Statements--
SELECT #Results = COUNT(ID) FROM Customers
WHERE FirstName LIKE '%a%'
SET #StartIndex = #StartIndex - 1 --Either do this here or in code, but be consistent
CREATE TABLE #Page(ROW INT IDENTITY(1,1) NOT NULL, id INT, sorting_1 SQL_VARIANT, sorting_2 SQL_VARIANT)
INSERT INTO #Page(ID, sorting_1, sorting_2)
SELECT TOP (#StartIndex + #PageSize)
ID,
CASE
WHEN #SortColumn='FirstName' AND #SortDirection='ASC' THEN CAST(FirstName AS SQL_VARIANT)
WHEN #SortColumn='LastName' AND #SortDirection='ASC' THEN CAST(LastName AS SQL_VARIANT)
ELSE NULL
END AS sort_1,
CASE
WHEN #SortColumn='FirstName' AND #SortDirection='DES' THEN CAST(FirstName AS SQL_VARIANT)
WHEN #SortColumn='LastName' AND #SortDirection='DES' THEN CAST(LastName AS SQL_VARIANT)
ELSE NULL
END AS sort_2
FROM (
SELECT
CustomerId AS ID,
FirstName,
LastName
FROM Customers
WHERE
FirstName LIKE '%a%'
) C
ORDER BY sort_1 ASC, sort_2 DESC, ID ASC;
SELECT
ID,
Customers.FirstName,
Customers.LastName
FROM #Page
INNER JOIN Customers ON
ID = Customers.CustomerId
WHERE ROW > #StartIndex AND ROW <= (#StartIndex + #PageSize)
ORDER BY ROW ASC
DROP TABLE #Page
I believe you'd need to perform a separate query to accomplish that unfortionately.
I was able to accomplish this at my previous position using some help from this page:
Paging in DotNet 2.0
They also have it pulling a row count seperately.
Here's what I do for paging: All of my big queries that need to be paged are coded as inserts into a temp table. The temp table has an identity field that will act in a similar manner to the row_number() mentioned above. I store the number of rows in the temp table in an output parameter so the calling code knows how many total records there are. The calling code also specifies which page it wants, and how many rows per page, which are selected out from the temp table.
The cool thing about doing it this way is that I also have an "Export" link that allows you to get all rows from the report returned as CSV above every grid in my application. This link uses the same stored procedure: you just return the contents of the temp table instead of doing the paging logic. This placates users who hate paging, and want to see everything, and want to sort it in a million different ways.