Getting data for a user in a month - sql

(The database is Postgres )
Let's say I have a table races with columns (user_name , race_time , race_speed , race_no).
Each users can have multiple races . I want to get data for each month and show for each month how many races are played in that month. But I also want to show which user played the most and which played has the maximum speed.
select
extract ( month from race_time) as month,
extract ( year from race_time) as year,
count(race_no) as total_races
from races r
group by year,month
order by year desc,month desc
The above query gives me each month and the total races but how can I find for each month which user played the most (that'd be one user)?
I hope someone could help me with this.

SQL Fiddle
with r as (
select
to_char(race_time, 'YYYY-MM') as month,
user_name,
count(*) as n_races,
max(race_speed) as max_speed
from races
group by 1, 2
), total_races as (
select sum(n_races) as total_races, month
from r
group by month
)
select *
from (
select distinct on (month)
month, user_name as user_most_races, n_races as most_races
from r
order by 1, 3 desc
) s
inner join (
select distinct on (month)
month, user_name as user_max_speed, max_speed
from r
order by 1, 3 desc
) q using (month)
inner join
total_races using(month)
order by month

SELECT COUNT(DISTINCT user_name), user_name, EXTRACT(month FROM race_time) AS month, EXTRACT(year FROM race_time) AS year
FROM races r
GROUP BY user_name, year, month
ORDER BY year desc, month desc

Try it!
USE [DATABASE_NAME]
GO
--this is calculating the which user played the most
CREATE FUNCTION [dbo].[GetHighestPlayedRaceUserByYearMonth]
(
-- Add the parameters for the function here
#Year INT, #Month INT
)
RETURNS NVARCHAR(50)
AS
BEGIN
-- Declare the return variable here
DECLARE #UserName NVARCHAR(50)
-- Add the T-SQL statements to compute the return value here
DECLARE #TempTbl TABLE(Played INT,UserName NVARCHAR(50))
INSERT INTO #TempTbl(Played,UserName)
SELECT
COUNT(race_no) AS Played,
[user_name] AS UserName
FROM Table_1
WHERE
YEAR(race_time)=#Year and
MONTH(race_time)=#Month
GROUP BY
MONTH(race_time),
YEAR(race_time),
[user_name]
ORDER BY
YEAR(race_time) DESC,
MONTH(race_time) DESC,
COUNT(race_no) DESC
DECLARE #MaxPlayed INT
SET #MaxPlayed=(SELECT TOP 1 Played FROM #TempTbl ORDER BY Played DESC)
DECLARE #TempTbl2 TABLE(ID INT IDENTITY(1,1) NOT NULL,UserName NVARCHAR(50))
INSERT INTO #TempTbl2
SELECT UserName FROM #TempTbl WHERE Played=#MaxPlayed
DECLARE #ROWCOUNT INT
SET #ROWCOUNT=1
SET #UserName=''
WHILE (SELECT COUNT(*) FROM #TempTbl2)>=#ROWCOUNT
BEGIN
IF (SELECT COUNT(*) FROM #TempTbl2)=#ROWCOUNT
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)
END
ELSE
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)+','
END
SET #ROWCOUNT=#ROWCOUNT+1
END
-- Return the result of the function
RETURN #UserName
END
GO
--this is calculating which user played with the maximum speed
CREATE FUNCTION [dbo].[GetMaxRaceSpeedUserByYearMonth]
(
-- Add the parameters for the function here
#Year INT, #Month INT
)
RETURNS NVARCHAR(50)
AS
BEGIN
-- Declare the return variable here
DECLARE #UserName NVARCHAR(50)
-- Add the T-SQL statements to compute the return value here
DECLARE #TempTbl TABLE(MaxRaceSpeed INT,UserName NVARCHAR(50))
INSERT INTO #TempTbl(MaxRaceSpeed,UserName)
SELECT
MAX(race_speed) AS MaxRaceSpeed,
[user_name] AS UserName
FROM Table_1
WHERE
YEAR(race_time)=#Year and
MONTH(race_time)=#Month
GROUP BY
MONTH(race_time),
YEAR(race_time),
[user_name]
ORDER BY
YEAR(race_time) DESC,
MONTH(race_time) DESC,
COUNT(race_no) DESC
DECLARE #MaxRaceSpeed INT
SET #MaxRaceSpeed=(SELECT TOP 1 MaxRaceSpeed FROM #TempTbl ORDER BY MaxRaceSpeed DESC)
DECLARE #TempTbl2 TABLE(ID INT IDENTITY(1,1) NOT NULL,UserName NVARCHAR(50))
INSERT INTO #TempTbl2
SELECT UserName FROM #TempTbl WHERE MaxRaceSpeed=#MaxRaceSpeed
DECLARE #ROWCOUNT INT
SET #ROWCOUNT=1
SET #UserName=''
WHILE (SELECT COUNT(*) FROM #TempTbl2)>=#ROWCOUNT
BEGIN
IF (SELECT COUNT(*) FROM #TempTbl2)=#ROWCOUNT
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)
END
ELSE
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)+','
END
SET #ROWCOUNT=#ROWCOUNT+1
END
-- Return the result of the function
RETURN #UserName
END
GO
--Select Query
SELECT
MONTH(race_time) AS [Month],
YEAR(race_time) AS [Year],
COUNT(race_no) AS RacePlayed,
dbo.GetHighestPlayedRaceUserByYearMonth(YEAR(race_time),MONTH(race_time)) AS MaximunPlayedUser,
dbo.GetMaxRaceSpeedUserByYearMonth(YEAR(race_time),MONTH(race_time)) AS MaximunSpeedUser
FROM TABLE_NAME
GROUP BY MONTH(race_time),YEAR(race_time)
ORDER BY YEAR(race_time) DESC,MONTH(race_time) DESC

Related

declaring multiple columns in a count value

I am trying to define multiple count values for a counter and corresponding value. Here is my code:
DECLARE #RequestDate AS DATE = '2017-04-20'
;
DECLARE #POCounter INT;
DECLARE #POMax INT;
DECLARE #NewDate DATE;
SET #POCounter = 0;
SET #POMax =
(
SELECT
CUSTOMERPONUMBER,
(
SELECT
COUNT(CUSTOMERPONUMBER)
FROM DailyOpenOrders$
WHERE
RequestDate < #RequestDate
)
FROM DailyOpenOrders$
WHERE
RequestDate < #RequestDate
GROUP BY
CUSTOMERPONUMBER
)
The #POMax counter is to help me update the date that many times for a specific customerPO. I intend to increase the POCounter in a loop until it reaches the #POmax counter for each customerPOnumber.
Am i doing this wrong? can someone help?
#POMax is a scalar variable and can only hold one value at a time.
If you want to hold a collection of values, a table variable is a good tool for that:
DECLARE #MyTable TABLE
(
ID int PRIMARY KEY IDENTITY(1,1),
CustomerNumber varchar(50),
[TheCount] int
)
INSERT INTO #MyTable(CustomerNumber, [TheCount])
SELECT CustomerNumber, COUNT(*)
FROM SomeTable
GROUP BY CustomerNumber
Now you can loop over #MyTable, and for each CustomerNumber, loop from 1 to TheCount...
DECLARE #MyID int
SET #MyID = (SELECT MIN(ID) FROM #MyTable)
WHILE #MyID is not null
BEGIN
SELECT * FROM #MyTable WHERE ID = #MyID
SET #MyID = (SELECT MIN(ID) FROM #MyTable WHERE #MyID < ID)
END

dynamic alias in sql server

I want query field with different alias in stored procedure
select COUNT(EmpCode) as CountEmp+#para
result shoud be
CountEmp1
45
CountEmp2
54
CountEmp1
76
Query loop in c# code:
select COUNT(EmpCode) where something = #something as CountEmp+#para
Approach without dynamic SQL:
--I create temp table for demonstration
DECLARE #some_table TABLE (
Something int,
EmpCode INT
)
INSERT INTO #some_table (Something, EmpCode)
VALUES (1, 10),(1, 22),(1, 12),(2, 12),(2, 30),(3, 65),(3, 15),(3, 11),(3, 5)
--Declare parameter we want to search
DECLARE #param int = 1
--Query
--In cte we select what we need based on parameter
;WITH cte AS (
SELECT 'CountEmp'+CAST(#param as nvarchar(10)) as SomeThing,
CAST(COUNT(EmpCode) as nvarchar(10)) as EmpCodeCount,
ROW_NUMBER() OVER (ORDER BY SomeThing ) as rn
FROM #some_table
WHERE SomeThing = #param
GROUP BY SomeThing
)
--And here comes UNION
SELECT SomeThing as Result
FROM (
SELECT SomeThing,rn
FROM cte
UNION ALL
SELECT EmpCodeCount ,rn
FROM cte
) as t
ORDER BY rn, SomeThing DESC
Output:
Result
------------------
CountEmp1
3
(2 row(s) affected)
Please try to make use of below code. Its working fine with SQL Server 2012.
IF OBJECT_ID ('temp..#Mytable') IS NOT NULL
CREATE TABLE #Mytable (ID INT IDENTITY (1,1),EmpCode INT)
DECLARE #max int ,#count int
SET #max =0;
DECLARE #str varchar(10)
INSERT #Mytable
(EmpCode)
VALUES
(10),
(45),
(35),
(63),
(56),
(65)
SET #count = (SELECT COUNT (ID) FROM #Mytable )
WHILE #count > #max
BEGIN
SET #max = #max+1
SET #str = CONVERT(varchar(10),#max)
EXEC('SELECT EmpCode AS Empcode'+#str+ ' FROM #Mytable WHERE ID = '+#str)
END

Replace Cursor Next number assignment Operation with Set based equivalent

Good day all,
I have the following cursor query and would like to replace it with a set based query to address performance issues.
DECLARE #EmpIDM CHAR(21);
DECLARE #EmpName CHAR(21);
DECLARE #EMPCatID INT;
DECLARE Assign_Emp SCROLL CURSOR
FOR
SELECT DISTINCT EMP
, EMPNAME
FROM HR_EMPLOYEES
SET NOCOUNT ON
OPEN Assign_Emp;
FETCH NEXT
FROM Assign_Emp
INTO #EmpIDM
, #EmpName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #EMPCatID = (
SELECT TOP 1 CategoryID
FROM Categories
)
UPDATE Categories
SET CategoryID = (CategoryID + 1) /*Increment Category ID for next Insert*/
INSERT INTO Table1 (
EmpNumber
, EmployeeName
, EmployeeCategoryID
)
VALUES (
#EmpIDM
, #EmpName
, #EMPCatID
)
FETCH NEXT
FROM Assign_Emp
INTO #EmpIDM
, #EmpName
END
CLOSE Assign_Emp;
CLOSE Assign_Emp;
SET NOCOUNT OFF
My challenge is adapting the following code segment into a set based operation
SET #EMPCatID = (
SELECT TOP 1 CategoryID
FROM Categories
)
UPDATE Categories
SET CategoryID = (CategoryID + 1) /*Increment Category ID for next Insert*/
I humbly appreciate any insight on how I can achieve this.
Many Thanks,
Re-write using temp. table with identity column:
declare #offset int
select #offset = isnull(max(CategoryID),0) from Categories
create table #data (
EmpNumber CHAR(21),
EmployeeName CHAR(21),
EmployeeCategoryID int identity
)
INSERT INTO #data (
EmpNumber
, EmployeeName)
SELECT DISTINCT EmpIDM
, EmpName
FROM HR_EMPLOYEES
insert into Table1 (
EmpNumber
, EmployeeName
, EmployeeCategoryID
) select
EmpNumber
, EmployeeName
, EmployeeCategoryID + #offset
from #data
update Categories
set CategoryID = (select max(EmployeeCategoryID) from #data) + #offset

update table after ;with() select statement

I use this procedure to select records with paging :
ALTER PROCEDURE [PorSun].[soalPagingByIdGroupIdUser] (
#IdGroup ,
#pageNo int,
#pageSize int)
AS
DECLARE #start int, #end int
SET #start = #pageSize*(#pageNo-1)+1
SET #end= #start+#pageSIze-1
;with Records as
(
SELECT
PorSun.soal.Id, PorSun.soal.IdUser, PorSun.soal.VisitCount
, ROW_NUMBER() over(order by PorSun.soal.Id) as RN
FROM PorSun.soal
WHERE (PorSun.soal.IdGroup=#IdGroup)
)
SELECT Records.Id, Records.IdUser, Records.VisitCount
FROM Records
WHERE RN between #start and #end and (Records.IdGroup=#IdGroup)
ORDER BY Records.Id desc
UPDATE [PorSun].[Soal]
SET [VisitCount] = [VisitCount]+1
WHERE Id IN (SELECT Id FROM Records)
There is no syntax error, but on execute error.
How is it possible?
You can use a table variable instead of a CTE:
DECLARE #Records TABLE
(
Id int,
IdUser int,
VisitCount int,
RN int
)
INSERT INTO #Records
SELECT
PorSun.soal.Id,
PorSun.soal.IdUser,
PorSun.soal.VisitCount,
ROW_NUMBER() over(order by PorSun.soal.Id) as RN
FROM PorSun.soal
WHERE (PorSun.soal.IdGroup=#IdGroup)
SELECT Id, IdUser, VisitCount
FROM #Records
WHERE RN between #start and #end and (IdGroup=#IdGroup)
ORDER BY Id desc
UPDATE [PorSun].[Soal]
SET [VisitCount] = [VisitCount]+1
WHERE Id IN (SELECT Id FROM #Records)

Delete duplicate records in SQL Server?

Consider a column named EmployeeName table Employee. The goal is to delete repeated records, based on the EmployeeName field.
EmployeeName
------------
Anand
Anand
Anil
Dipak
Anil
Dipak
Dipak
Anil
Using one query, I want to delete the records which are repeated.
How can this be done with TSQL in SQL Server?
You can do this with window functions. It will order the dupes by empId, and delete all but the first one.
delete x from (
select *, rn=row_number() over (partition by EmployeeName order by empId)
from Employee
) x
where rn > 1;
Run it as a select to see what would be deleted:
select *
from (
select *, rn=row_number() over (partition by EmployeeName order by empId)
from Employee
) x
where rn > 1;
Assuming that your Employee table also has a unique column (ID in the example below), the following will work:
delete from Employee
where ID not in
(
select min(ID)
from Employee
group by EmployeeName
);
This will leave the version with the lowest ID in the table.
Edit
Re McGyver's comment - as of SQL 2012
MIN can be used with numeric, char, varchar, uniqueidentifier, or datetime columns, but not with bit columns
For 2008 R2 and earlier,
MIN can be used with numeric, char, varchar, or datetime columns, but not with bit columns (and it also doesn't work with GUID's)
For 2008R2 you'll need to cast the GUID to a type supported by MIN, e.g.
delete from GuidEmployees
where CAST(ID AS binary(16)) not in
(
select min(CAST(ID AS binary(16)))
from GuidEmployees
group by EmployeeName
);
SqlFiddle for various types in Sql 2008
SqlFiddle for various types in Sql 2012
You could try something like the following:
delete T1
from MyTable T1, MyTable T2
where T1.dupField = T2.dupField
and T1.uniqueField > T2.uniqueField
(this assumes that you have an integer based unique field)
Personally though I'd say you were better off trying to correct the fact that duplicate entries are being added to the database before it occurs rather than as a post fix-it operation.
DELETE
FROM MyTable
WHERE ID NOT IN (
SELECT MAX(ID)
FROM MyTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)
WITH TempUsers (FirstName, LastName, duplicateRecordCount)
AS
(
SELECT FirstName, LastName,
ROW_NUMBER() OVER (PARTITIONBY FirstName, LastName ORDERBY FirstName) AS duplicateRecordCount
FROM dbo.Users
)
DELETE
FROM TempUsers
WHERE duplicateRecordCount > 1
WITH CTE AS
(
SELECT EmployeeName,
ROW_NUMBER() OVER(PARTITION BY EmployeeName ORDER BY EmployeeName) AS R
FROM employee_table
)
DELETE CTE WHERE R > 1;
The magic of common table expressions.
Try
DELETE
FROM employee
WHERE rowid NOT IN (SELECT MAX(rowid) FROM employee
GROUP BY EmployeeName);
If you're looking for a way to remove duplicates, yet you have a foreign key pointing to the table with duplicates, you could take the following approach using a slow yet effective cursor.
It will relocate the duplicate keys on the foreign key table.
create table #properOlvChangeCodes(
id int not null,
name nvarchar(max) not null
)
DECLARE #name VARCHAR(MAX);
DECLARE #id INT;
DECLARE #newid INT;
DECLARE #oldid INT;
DECLARE OLVTRCCursor CURSOR FOR SELECT id, name FROM Sales_OrderLineVersionChangeReasonCode;
OPEN OLVTRCCursor;
FETCH NEXT FROM OLVTRCCursor INTO #id, #name;
WHILE ##FETCH_STATUS = 0
BEGIN
-- determine if it should be replaced (is already in temptable with name)
if(exists(select * from #properOlvChangeCodes where Name=#name)) begin
-- if it is, finds its id
Select top 1 #newid = id
from Sales_OrderLineVersionChangeReasonCode
where Name = #name
-- replace terminationreasoncodeid in olv for the new terminationreasoncodeid
update Sales_OrderLineVersion set ChangeReasonCodeId = #newid where ChangeReasonCodeId = #id
-- delete the record from the terminationreasoncode
delete from Sales_OrderLineVersionChangeReasonCode where Id = #id
end else begin
-- insert into temp table if new
insert into #properOlvChangeCodes(Id, name)
values(#id, #name)
end
FETCH NEXT FROM OLVTRCCursor INTO #id, #name;
END;
CLOSE OLVTRCCursor;
DEALLOCATE OLVTRCCursor;
drop table #properOlvChangeCodes
delete from person
where ID not in
(
select t.id from
(select min(ID) as id from person
group by email
) as t
);
Please see the below way of deletion too.
Declare #Employee table (EmployeeName varchar(10))
Insert into #Employee values
('Anand'),('Anand'),('Anil'),('Dipak'),
('Anil'),('Dipak'),('Dipak'),('Anil')
Select * from #Employee
Created a sample table named #Employee and loaded it with given data.
Delete aliasName from (
Select *,
ROW_NUMBER() over (Partition by EmployeeName order by EmployeeName) as rowNumber
From #Employee) aliasName
Where rowNumber > 1
Select * from #Employee
Result:
I know, this is asked six years ago, posting just incase it is helpful for anyone.
Here's a nice way of deduplicating records in a table that has an identity column based on a desired primary key that you can define at runtime. Before I start I'll populate a sample data set to work with using the following code:
if exists (select 1 from sys.all_objects where type='u' and name='_original')
drop table _original
declare #startyear int = 2017
declare #endyear int = 2018
declare #iterator int = 1
declare #income money = cast((SELECT round(RAND()*(5000-4990)+4990 , 2)) as money)
declare #salesrepid int = cast(floor(rand()*(9100-9000)+9000) as varchar(4))
create table #original (rowid int identity, monthyear varchar(max), salesrepid int, sale money)
while #iterator<=50000 begin
insert #original
select (Select cast(floor(rand()*(#endyear-#startyear)+#startyear) as varchar(4))+'-'+ cast(floor(rand()*(13-1)+1) as varchar(2)) ), #salesrepid , #income
set #salesrepid = cast(floor(rand()*(9100-9000)+9000) as varchar(4))
set #income = cast((SELECT round(RAND()*(5000-4990)+4990 , 2)) as money)
set #iterator=#iterator+1
end
update #original
set monthyear=replace(monthyear, '-', '-0') where len(monthyear)=6
select * into _original from #original
Next I'll create a Type called ColumnNames:
create type ColumnNames AS table
(Columnnames varchar(max))
Finally I will create a stored proc with the following 3 caveats:
1. The proc will take a required parameter #tablename that defines the name of the table you are deleting from in your database.
2. The proc has an optional parameter #columns that you can use to define the fields that make up the desired primary key that you are deleting against. If this field is left blank, it is assumed that all the fields besides the identity column make up the desired primary key.
3. When duplicate records are deleted, the record with the lowest value in it's identity column will be maintained.
Here is my delete_dupes stored proc:
create proc delete_dupes (#tablename varchar(max), #columns columnnames readonly)
as
begin
declare #table table (iterator int, name varchar(max), is_identity int)
declare #tablepartition table (idx int identity, type varchar(max), value varchar(max))
declare #partitionby varchar(max)
declare #iterator int= 1
if exists (select 1 from #columns) begin
declare #columns1 table (iterator int, columnnames varchar(max))
insert #columns1
select 1, columnnames from #columns
set #partitionby = (select distinct
substring((Select ', '+t1.columnnames
From #columns1 t1
Where T1.iterator = T2.iterator
ORDER BY T1.iterator
For XML PATH ('')),2, 1000) partition
From #columns1 T2 )
end
insert #table
select 1, a.name, is_identity from sys.all_columns a join sys.all_objects b on a.object_id=b.object_id
where b.name = #tablename
declare #identity varchar(max)= (select name from #table where is_identity=1)
while #iterator>=0 begin
insert #tablepartition
Select distinct case when #iterator=1 then 'order by' else 'over (partition by' end ,
substring((Select ', '+t1.name
From #table t1
Where T1.iterator = T2.iterator and is_identity=#iterator
ORDER BY T1.iterator
For XML PATH ('')),2, 5000) partition
From #table T2
set #iterator=#iterator-1
end
declare #originalpartition varchar(max)
if #partitionby is null begin
select #originalpartition = replace(b.value+','+a.type+a.value ,'over (partition by','') from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1
select #partitionby = a.type+a.value+' '+b.type+a.value+','+b.value+') rownum' from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1
end
else
begin
select #originalpartition=b.value +','+ #partitionby from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1
set #partitionby = (select 'OVER (partition by'+ #partitionby + ' ORDER BY'+ #partitionby + ','+b.value +') rownum'
from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1)
end
exec('select row_number() ' + #partitionby +', '+#originalpartition+' into ##temp from '+ #tablename+'')
exec(
'delete a from _original a
left join ##temp b on a.'+#identity+'=b.'+#identity+' and rownum=1
where b.rownum is null')
drop table ##temp
end
Once this is complied, you can delete all your duplicate records by running the proc. To delete dupes without defining a desired primary key use this call:
exec delete_dupes '_original'
To delete dupes based on a defined desired primary key use this call:
declare #table1 as columnnames
insert #table1
values ('salesrepid'),('sale')
exec delete_dupes '_original' , #table1