I'm getting a rollup of the same value twice - sql-server

I have table sellers where I list every single one of my sellers, and I added the column objective recently.
id |name | team_leader_id | team_leader | objective
--------------------------------------------------
1 |John | 50 | Mark | 30
2 |Jane | 66 | Ryu | 30
3 |Angela | 66 | Ryu | 45
4 |Arthur | 190 | Carol | 35
5 |Anthony| 20 | Adam | 50
I have another table sales where I link my sellers table on seller_id.
sale_id |seller_id |seller_name |item
-------------------------------------
56879 |2 |Jane |4P
23512 |2 |Jane |3P
54827 |2 |Jane |3P
12345 |5 |Anthony |4P
55435 |4 |Arthur |GSM
The query I'm trying is:
SELECT coalesce(seller.team_leader,'') team_leader,
coalesce(sales.seller_name,'TOTAL') seller_name,
seller.objective,
count(*) as quantity
FROM sales
JOIN seller ON seller.id = sales.seller_id
WHERE seller.team_leader_id = 66
GROUP BY seller.team_leader, ROLLUP(sales.seller_name), seller.objective
I noticed that the result I'm getting a duplicate of every line that now has an objective.
I think the problem is because my objective column is new, and I'm joining my sales table with my seller table, it counts the records I had before creating the objective column separately.
So, my expected result would be
team_leader | seller_name | objective | quantity
------------------------------------------------
Ryu | TOTAL | | 3
| Jane | 30 | 3
| Angela | 45 | 0
But this is what I'm getting
team_leader | seller_name | objective | quantity
------------------------------------------------
Ryu | TOTAL | | 1
Ryu | TOTAL | 30 | 2
| Jane | | 1
| Jane | 30 | 2
| Angela | 45 | 0
When the objective appears blank with Jane, it is a sale that she did before I added the objective column.

You can try the following :
SELECT case when seller.name is null then max(seller.team_leader) else '' end as team_leader,
isnull(seller.name,'TOTAL') seller_name,
case when seller.name is null then '' else sum(seller.objective) end objective,
count(sales.seller_id) as quantity
FROM seller
LEFT JOIN sales ON seller.id = sales.seller_id
WHERE seller.team_leader_id = 66
group by ROLLUP(seller.name)
order by team_leader desc, quantity desc
OR if you are okay with not using ROLLUP, you can get the exact same result using the following query.
;with cte as (
SELECT max(team_leader) team_leader,
max(name) seller_name,
max(objective) objective,
count(seller_id) as quantity
FROM seller
LEFT JOIN sales ON seller.id = sales.seller_id
WHERE seller.team_leader_id = 66
GROUP BY seller.id
)
SELECT team_leader, 'TOTAL' seller_name, '' objective, sum(quantity) quantity
FROM cte
GROUP BY team_leader
UNION ALL
SELECT '', seller_name, objective, quantity
FROM cte

Related

Ranking within multiple groups & Efficient query for multiple table updates

I'm trying to add rank by sales by month and also change the date column to a 'month end' field that would show only last day of month.
Can i do two sets in a row like that without adding an update?
I'm looking for top 2 within each month - does limit and group by work?
I feel like this is right and most efficient query, but its not working - any help appreciated!!
UPDATE table1
SET DATE=EOMONTH(DATE) AS MONTH_END;
ALTER TABLE table1
ADD COLUMN RANK INT AFTER sales;
UPDATE table1
SET RANK=
RANK() OVER(PARTITION BY cust ORDER BY sales DESC);
LIMIT 2
orig table
+------+----------+-------+--+
| CUST | DATE | SALES | |
+------+----------+-------+--+
| 36 | 3-5-2018 | 50 | |
| 37 | 3-15-18 | 100 | |
| 38 | 3-25-18 | 65 | |
| 37 | 4-5-18 | 95 | |
| 39 | 4-21-18 | 500 | |
| 40 | 4-45-18 | 199 | |
+------+----------+-------+--+
desired output
+------+-----------+-------+------+
| CUST | Month End | SALES | Rank |
+------+-----------+-------+------+
| | | | |
| 37 | 3-31-18 | 100 | 1 |
| 38 | 3-31-18 | 65 | 2 |
| 39 | 4-30-18 | 500 | 1 |
| 40 | 4-30-18 | 199 | 2 |
+------+-----------+-------+------+
I do not know why you want EOMONTH as a stored value, but what you have for that will work.
I would not use [rank] as a column name as I avoid any words that are used in SQL, maybe [sales_rank] or similar.
ALTER TABLE table1
ADD COLUMN [sales_rank] INT AFTER sales;
with cte as (
select
cust
, DENSE_RANK() OVER(PARTITION BY cust ORDER BY sales DESC) as ranking
from table1
)
update cte
set sales_rank = ranking
where ranking < 3
;
LIMIT 2 is not something that can be used in SQL Server by the way, and it sure can't be used "per grouping". When you use a "window function" such as rank() or dense_rank() you can use the output of those in the where clause of the next "layer". i.e. use those functions in a subquery (or cte) and then use a where clause to filter rows by the calculated values.
Also note I used dense_rank() to guarantee that no rank numbers are skipped, so that the subsequent where clause will be effective.

Exclude Secondary ID Records from Original SELECT

I'm relatively new to SQL and am running into a lot of issues trying to figure this one out. I've tried using a LEFT JOIN, and dabbled in using functions to get this to work but to no avail.
For every UserID, if there is a NULL value, I need to remove all records of the Product ID for that UserID from my SELECT.
I am using SQL Server 2014.
Example Table
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 578 | 1 | 1/15/2017 |
| 578 | 1 | NULL |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Desired Output
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Try the following:
SELECT a.userid, a.productid, a.datetermed
FROM yourtable a
LEFT OUTER JOIN (SELECT userid, productid, datetermed FROM yourtable WHERE
datetermed is null) b
on a.userid = b.userid and a.productid = b.productid
WHERE b.userid is not null
This will left outer join all records with a null date to their corresponding UserID and ProductID records. If you only take records that don't have an associated UserID and ProductID in the joined table, you should only be left with records that don't have a null date.
You can use this WHERE condition:
SELECT
UserID,ProducID,DateTermed
FROM
[YourTableName]
WHERE
(CONVERT(VARCHAR,UserId)+
CONVERT(VARCHAR,ProductID) NOT IN (
select CONVERT(VARCHAR,UserId)+ CONVERT(VARCHAR,ProductID)
from
[YourTableName]
where DateTermed is null)
)
When you concatenate the UserId and the ProductId get a unique value for each pair, then you can use them as a "key" to exclude the "pairs" that have the null value in the DateTermed field.
Hope this help.

Delete partial dulicate rows - sql

I have some troubles with deleting partial duplicate rows
The structure is like this:
+-----+--------+--+-----------+--+------+
| id | userid | | location | | week |
+-----+--------+--+-----------+--+------+
| 1 | 001 | | amsterdam | | 11 |
| 2 | 001 | | amsterdam | | 23 |
| 3 | 002 | | berlin | | 28 |
| 4 | 002 | | berlin | | 22 |
| 5 | 003 | | paris | | 19 |
| 6 | 003 | | paris | | 35 |
+-----+--------+--+-----------+--+------+
I only need to keep one row from each userid, it doesn't matter which week number it has.
Thanks,
Maxcim
This should work across most databases:
DELETE
FROM yourTable
WHERE id <> (SELECT MIN(id)
FROM yourTable t
WHERE t.userid = userid)
This query would delete from each userid group all records except for the record having the lowest id for that group. I assume that id is a unique column.
This method is tested, try it.
We are getting the number of rows occuring at each record, and then we are deleting only the ones with more than 1 row occruring... keeping the original one.
BEGIN TRANSACTION
SELECT UserID, Location,
RN = ROW_NUMBER()OVER(PARTITION BY UserID, Location ORDER BY UserID, Location)
into #test1
FROM dbo.MyTbl
Delete MyTbl
From MyTbll
INNER JOIN #test1
ON #test1.UserID= MyTbl.UserID
WHERE RN > 1
if ##Error <> 0 GOTO Errlbl
Commit Transaction
RETURN
Errlbl:
RollBack Transaction
GO

Return records based on min data in source and target if conditions satisfied in sql server

Hi I have data in sql server
Table : emp
Empid | deptid | doj | loc | Status|guid
1 | 10 | 2013-09-25 | hyd | 5 |10
1 | 10 | 2014-03-25 | che | 5 |11
1 | 10 | 2014-04-09 | pune | 5 |12
1 | 10 | 2015-01-22 | pune | 5 |13
2 | 20 | 2015-12-13 | beng | 5 |14
2 | 20 | 2014-12-17 | chen | 5 |15
2 | 20 | 2010-10-15 | beng | 4 |16
Table : empref
empid | deptid | startdate | status |guid
1 | 10 | 2013-10-02 | 2 |1
1 | 10 | 2014-04-09 | 2 |2
1 | 10 | 2015-12-09 | 1 |3
1 | 10 | 2015-01-30 | 2 |4
2 | 20 | 2015-12-14 | 2 |2
2 | 20 | 2015-12-15 | 2 |3
Both tables have common columns Empid + deptid
We need to consider emp table status=5 related records compare with empref table status=2
related records and emp table doj <= startdate --empref table and days difference between less than or equal to 30 days
If we find multiple records fall within 30 days in empref table startdate then we need to consider min(startdate) corresponding records
and that records need to be considered as update. Remain status values 4 or 1 no need in the return result set at this time.
If emp table status=5 related records compare with empref table status=2
related records and emp table doj <= startdate --empref table and daysdiffernce between less than or equal 30 days
If we find multiple records fall with in 30days in emp table doj then we need to consider min(doj) corresponding records
and that record needs to be considered as update in the filter column and guid information from empref table.
Remaining records considered as insert records in the filter column and guid information from emptable.
if emp table doj <=startdate--empref table condition not satisfied or
daysdiffernce not between less than or equal 30 days then that records we need to consider insert in the filter column
based on above tables I want output like below
Empid | Deptid | loc | Status | Filter | Doj |guid
1 | 10 | hyd | 5 | Update | 2013-09-25|1
1 | 10 | che | 5 | insert | 2014-03-25|11 ------min(startdate) corresponding record
1 | 10 | pune | 5 | update | 2014-04-09|2 --------mul
1 | 10 | Pune | 5 | update | 2015-01-22|4
2 | 20 | beng | 5 | update | 2015-12-13|2 --------------min(doj) record
2 | 20 | chen | 5 | insert | 2014-12-17|15
2 | 20 | beng | 4 | insert | 2010-10-15|16 -----this record not fall the above conditions
I tried like below
select s1.*
,'Update' as Filter from emp e join empref er
on e.empid=er.empid and
e.deptid=t.deptid
and e.status='5'
and er.status='2' and
e.doj<=er.startdate and datediff(dd,er.startdate,e.doj)*-1<=30
group by er.startdate,
e.empid,e.deptid.e.doj,e.loc
having e.startdate= min(er.startdate)
In the above query not given expected result. Please help me write this query to achieve this task in sql server.
It seems like the query you supplied is very close. Here is what I quickly put together. I haven't tested it against a lot of the possible options.
select e.Empid, er.deptid, e.loc, e.[status],
case when DATEDIFF(DAY, e.doj, er.startdate) <= 0 THEN 'INSERT'
ELSE 'UPDATE' END [DaysOffset],
e.doj
FROM #emp e inner join #empref ER
on e.Empid = er.empid and
e.deptid = er.deptid
where e.[status] = 5 and er.[status] = 2
and e.doj <= er.startdate and
DATEDIFF(DAY, e.doj, er.startdate) <= 30
The CASE statement is where it determines when the record is flagged for INSERT or UPDATE. With the datediff in the WHERE clause, it will only return records that are 30 days or less.

Sql Total Sum using no of row counts in a table

i am trying to find the total marks for each student based on StudentId and MarksTypeId row count.
i have 3 tables
MarksType
----------------------------------
MarksTypeId | MarkType | Marks
----------------------------------
1 | Writing | 10
2 | Drawing | 30
3 | Singing | 20
----------------------------------
Students
--------------------------------
StudentId | Name | Address
--------------------------------
1 | John | USA
2 | Raja | India
3 | Paul | AUS
--------------------------------
MarksDetails -- Has two foreign keys
-------------------------------------------------------
MarksDetailsId | MarksTypeId | StudentId | Date
-------------------------------------------------------
1 | 3 | 1 | 18 jan
2 | 3 | 1 | 18 jan
3 | 1 | 3 | 19 jan
-------------------------------------------------------
This is my Desired Result :
------------------------------------
StudentId | Name | Total Marks
------------------------------------
1 | John | 40
2 | Raja | 0
3 | Paul | 10
------------------------------------
i mean if John sang two times a day, so using StudentId and MarksTypeId, i need his total Marks as result.
so far i did the following:
select Sum(MarksType.Marks) from MarksType inner join MarksDetails on MarksType.MarksTypeId=1
but the sum returns wrong total,
UPDATED OTHER ATTEMPTS:
this results total row count for each studentid
select MarksDetails.StudentId , COUNT(MarksDetails.StudentId ) as count from MarksDetails
group by MarksDetails.StudentId
this results Marks, with studentid and markstypeid
select MarksType.Marks, MarksType.MarksTypeId , MarksDetails.StudentId from MarksType inner join MarksDetails on MarksType.MarksTypeId = MarksDetails.StudentId
may i know what i am doing wrong.
Any help would be Great.
finally with kind guidance from sql developers and after making some research, here is the query with desired result, maybe useful for someone, enjoy !
SELECT Student.StudentId, Student.Name, sum(MarksType.Marks) as TotalMarks
FROM
MarksType
INNER JOIN
MarksDetails on MarksType.MarksTypeId = MarksDetails.MarksTypeId
INNER JOIN
Student on Student.StudentId = MarksDetails.StudentId
group by Student.Name,Student.StudentId

Resources