skip records based on columns condition - sql-server

I have a question in sql server
table name : Emp
Id |Pid |Firstname| LastName | Level
1 |101 | Ram |Kumar | 3
1 |100 | Ravi |Kumar | 2
2 |101 | Jaid |Balu | 10
1 |100 | Hari | Babu | 5
1 |103 | nani | Jai |44
1 |103 | Nani | Balu |10
3 |103 |bani |lalu |20
Here need to retrieve unique records based on id and Pid columns and records which have duplicate records need to skip.
Finally I want output like below
Id |Pid |Firstname| LastName | Level
1 |101 | Ram |Kumar | 3
2 |101 | Jaid |Balu | 10
3 |103 |bani |lalu |20
I found duplicate records based on below query
select id,pid,count(*) from emp group by id,pid having count(*) >=2
this query get duplicated records 2 that records need to skip to retrieve output
please tell me how to write query to achieve this task in sql server.

Since your output is based on unique ID and PID which do not have any duplicate value, You can use COUNT with partition to achieve your desired result.
SQL Fiddle
Sample Data
CREATE TABLE Emp
([Id] int, [Pid] int, [Firstname] varchar(4), [LastName] varchar(5), [Level] int);
INSERT INTO Emp
([Id], [Pid], [Firstname], [LastName], [Level])
VALUES
(1, 101, 'Ram', 'Kumar', 3),
(1, 100, 'Ravi', 'Kumar', 2),
(2, 101, 'Jaid', 'Balu', 10),
(1, 100, 'Hari', 'Babu', 5),
(1, 103, 'nani', 'Jai', 44),
(1, 103, 'Nani', 'Balu', 10),
(3, 103, 'bani', 'lalu', 20);
Query
SELECT *
FROM
(
SELECT *,rn = COUNT(*) OVER(PARTITION BY ID,PID)
FROM Emp
) Emp
WHERE rn = 1
Output
| Id | Pid | Firstname | LastName | Level |
|----|-----|-----------|----------|-------|
| 1 | 101 | Ram | Kumar | 3 |
| 2 | 101 | Jaid | Balu | 10 |
| 3 | 103 | bani | lalu | 20 |

Related

Get all course, chapter in one column SQL Server

Course
+-----+----------+
| id | c_name |
+-----+----------+
| 1 | course1 |
| 7 | course2 |
+-----+----------+
Chapter
+-----+----------+------------+
| id | Ch_name | c_id |
+-----+----------+------------+
| 3 | Chapter1 | 1 |
| 9 | Chapter2 | 7 |
| 11 | Chapter3 | 1 |
| 17 | Chapter4 | 1 |
+-----+----------+------------+
I'm trying to select all data so that I can generate the following output:
+-----+-- |
|Course |
+-----+-- |
|Course1 |
|Chapter1 |
|Chapter3 |
|Chapter4 |
| |
|Course2 |
|Chapter2 |
I have tried in this way:
select
c.CourseID ,
'Course' as table_name,
c.CourseName as Course,
'' as Chapter
from [MstCourse]c
union
select
s.CourseID,
'Chapter' as table_name,
c.CourseName as Course,
s.ChapterName as Chapter
from [MstCourse] c
inner JOIN [ManageChapter] s ON c.CourseID= s.CourseID
order by Course, Chapter
But I am not getting the results in a single column.
You could achieve this with a group by ... with rollup clause.
Sample data
create table course
(
id int,
name nvarchar(10)
);
insert into course(id, name) values
(1, 'Course1'),
(7, 'Course2');
create table chapter
(
id int,
name nvarchar(10),
c_id int
);
insert into chapter(id, name, c_id) values
(3 , 'Chapter1', 1),
(9 , 'Chapter2', 7),
(11, 'Chapter3', 1),
(17, 'Chapter4', 1);
Solution
select coalesce(ch.Name, co.Name) as [Course]
from course co
join chapter ch
on ch.c_id = co.id
group by co.Name, ch.Name with rollup
having grouping(co.Name) <> 1
order by co.Name, ch.Name;
For some background on how this solution works, have a look at this fiddle.

Separate comma values into individual values

I need to separate columns in SQL Server
Table: columnsseparates
CREATE TABLE [dbo].[columnsseparates](
[id] [varchar](50) NULL,
[name] [varchar](500) NULL
)
INSERT [dbo].[columnsseparates] ([id], [name]) VALUES (N'1,2,3,4', N'abc,xyz,mn')
GO
INSERT [dbo].[columnsseparates] ([id], [name]) VALUES (N'4,5,6', N'xy,yz')
GO
INSERT [dbo].[columnsseparates] ([id], [name]) VALUES (N'7,100', N'yy')
INSERT [dbo].[columnsseparates] ([id], [name]) VALUES (N'101', N'oo,yy')
GO
based on above data I want output like below:
id | Name
1 |abc
2 |xyz
3 |mn
4 |null
4 |xy
5 |yz
6 |null
7 |yy
100 |null
101 |oo
null |yy
How to achieve this task in SQL Server?
Storing non-atomic values in column is a sign that schema should be normalised.
Naive approach using PARSENAME(up to 4 comma separated values):
SELECT DISTINCT s.id, s.name
FROM [dbo].[columnsseparates]
CROSS APPLY(SELECT REVERSE(REPLACE(id,',','.')) id,REVERSE(REPLACE(name, ',','.')) name) sub
CROSS APPLY(VALUES (REVERSE(PARSENAME(sub.id,1)), REVERSE(PARSENAME(sub.name,1))),
(REVERSE(PARSENAME(sub.id,2)), REVERSE(PARSENAME(sub.name,2))),
(REVERSE(PARSENAME(sub.id,3)), REVERSE(PARSENAME(sub.name,3))),
(REVERSE(PARSENAME(sub.id,4)), REVERSE(PARSENAME(sub.name,4)))
) AS s(id, name)
ORDER BY s.id;
db<>fiddle demo
Output:
+------+------+
| id | name |
+------+------+
| | |
| | yy |
| 1 | abc |
| 100 | |
| 101 | oo |
| 2 | xyz |
| 3 | mn |
| 4 | |
| 4 | xy |
| 5 | yz |
| 6 | |
| 7 | yy |
+------+------+
If you have more than 4 values, then you'll to use a string splitter that can return the ordinal value. I use delimitedsplit8k_LEAD here:
WITH Ids AS(
SELECT cs.id,
cs.name,
DS.ItemNumber,
DS.Item
FROM dbo.columnsseparates cs
CROSS APPLY dbo.DelimitedSplit8K_LEAD (cs.id,',') DS),
Names AS (
SELECT cs.id,
cs.name,
DS.ItemNumber,
DS.Item
FROM dbo.columnsseparates cs
CROSS APPLY dbo.DelimitedSplit8K_LEAD (cs.[name],',') DS)
SELECT I.Item AS ID,
N.Item AS [Name]
FROM Ids I
FULL OUTER JOIN Names N ON I.id = N.id
AND I.ItemNumber = N.ItemNumber
ORDER BY CASE WHEN I.Item IS NULL THEN 1 ELSE 0 END,
TRY_CONVERT(int,I.Item);

How to Select all Entrys from one Table, and SUM a subset of another table

I have a larger Database with Times that employees entered. They enter an activity, when it was and how long they spent on it, as well as a customer.
I'm now trying to return a table with all employees, that Sums their times, but only if it's timed for a subset of Customers. I can get either a table with The Correct times, but employees that didn't enter any time are omitted, or I get all employees but with the sum time from all customers.
The tables I have are:
EMPLOYEE for the employees
ACTIVITY for all activities
CUSTOMER for the customers
To have some "example Data":
| EMPLOYEE | | ACTIVITY |
+------------+---------+ +------------+------------+------------+
| I_EMPLOYEE | S_NAME1 | | I_EMPLOYEE | I_CUSTOMER | N_DURETIME |
+------------+---------+ +------------+------------+------------+
| 1 | A | | 1 | 1 | 5 |
| 2 | B | | 2 | 3 | 10 |
| 3 | C | | 1 | 3 | 15 |
+------------+---------+ | 3 | 2 | 10 |
| 1 | 2 | 10 |
+------------+------------+------------+
What i'd expect to get when i want all times except Customer 2:
+----------+----------+
| EMPLOYEE | DURETIME |
+----------+----------+
| 1 | 20 |
| 2 | 10 |
| 3 | - |
+----------+----------+
I get either of those two out:
+----------+----------+ +----------+----------+
| EMPLOYEE | DURETIME | | EMPLOYEE | DURETIME |
+----------+----------+ +----------+----------+
| 1 | 20 | | 1 | 30 |
| 2 | 10 | | 2 | 10 |
+----------+----------+ | 3 | 10 |
+----------+----------+
To get the correct times i use the following:
SELECT emp.S_NAME1 AS Mitarbeiter, SUM(act.N_DURETIME)/60 as Zeit
FROM EMPLOYEE AS emp
LEFT JOIN ACTIVITY AS act on act.I_EMPLOYEE = emp.I_EMPLOYEE
LEFT JOIN CUSTOMER AS cust on cust.I_CUSTOMER = act.I_CUSTOMER
WHERE cust.CUSTNO NOT '2'
to get the full list of employees i used:
SELECT emp.S_NAME1 AS Mitarbeiter, SUM(act.N_DURETIME)/60 as Zeit
FROM EMPLOYEE AS emp
LEFT JOIN ACTIVITY AS act on act.I_EMPLOYEE = emp.I_EMPLOYEE
LEFT JOIN CUSTOMER AS cust on cust.I_CUSTOMER = act.I_CUSTOMER AND cust.CUSTNO NOT '2'
So, depending on whether I put my "Customer Filter" in the JOIN or the WHERE statement, I get half of the correct table. How can I combine those to get the correct output?
Create Table #emp
(
i_emp Int,
s_name1 Char(1)
)
Insert Into #emp Values
(1,'A'),
(2,'B'),
(3,'C')
Create Table #Activity
(
i_emp Int,
i_cust Int,
n_duretime Int
)
Insert Into #Activity Values
(1,1,5),
(2,3,10),
(1,3,15),
(3,2,10),
(1,2,10)
Query
Select
e.i_emp,
Sum(Case When a.i_cust = 2 Then Null Else a.n_duretime End) As durationTot
From
#emp e Left Join
#Activity a On e.i_emp = a.i_emp
Group By
e.i_emp
Result:
i_emp durationTot
1 20
2 10
3 NULL
You can try the following query
create table Employee(I_EMPLOYEE int, S_NAME1 char(1))
insert into Employee Values (1, 'A'),(2, 'B'),(3, 'C')
create table ACTIVITY (I_EMPLOYEE int, I_CUSTOMER int, N_DURETIME int)
insert into ACTIVITY Values(1, 1, 5 ),( 2, 3, 10), (1, 3, 15), ( 3, 2, 10), ( 1 , 2 , 10 )
select EMPLOYEE, sum(isnull(DURETIME, 0)) as DURETIME from(
select EMPLOYEE.S_NAME1 as EMPLOYEE, case I_Customer when 2 then 0 else N_DURETIME end as DURETIME from activity
inner join Employee on activity.I_EMPLOYEE = Employee.I_EMPLOYEE
)a group by EMPLOYEE
Below is the output
I_EMPLOYEE EMPLOYEE DURETIME
--------------------------------
1 A 20
2 B 10
3 C 0

Pairs of duplicate, identical rows. Update just one of them

Working on pretty large table in SQL-Server. Table has some identical rows. I need to remove duplicate rows. Problem is I cannot alter this table i.e. to create an ID column.
I could update one column value of the other row on pairs of duplicates. Then delete afterwards using this value.
How to update only one these rows?
For example: Firstly / lastly inserted, First occurrence, newest / oldest..
Thanks!
table Structure
NrValue | Comment | Value1 | Value2 | Value3 |
--------|-----------|-----------|-----------|---------------|
00000 | data0 | zz | top | vivalasvegas|
00100 | NULL | N/A | sex | no |
00100 | NULL | N/A | sex | no |
00200 | NULL | female | sex | yes |
00200 | NULL | female | sex | yes |
00300 | NULL | male | sex | yesplease |
00300 | NULL | male | sex | yesplease |
00400 | data21 | M | -- | na |
00500 | NULL | F | ezig | na |
So, I could use 'Comment' -column to update but I cannot touch other than duplicate rows. I know by NrValue which rows can be updated.
Result would be:
NrValue | Comment | Value1 | Value2 | Value3 |
--------|-----------|-----------|-----------|---------------|
00000 | data0 | zz | top | vivalasvegas|
00100 | 1 | N/A | sex | no |
00100 | 2 | N/A | sex | no |
00200 | 3 | female | sex | yes |
00200 | 4 | female | sex | yes |
00300 | 5 | male | sex | yesplease |
00300 | 6 | male | sex | yesplease |
00400 | data21 | M | -- | na |
00500 | NULL | F | ezig | na |
Lastly I delete rows where NrValue = 00100, 00200 or 00300 AND Comment = 2, 4 or 6.
Use something like
ROW_NUMBER() OVER(PARTITION BY AllRelevantColumns ORDER BY SomeOrderCriteria)
This will generate a 1 for all rows, but duplicates get a 2 (or a 3 ...)
You might place this value in a new column or use this for cleaning...
UPDATE Following your test data...
DECLARE #mockup TABLE(NrValue INT,Comment VARCHAR(100),Value1 VARCHAR(100),Value2 VARCHAR(100),Value3 VARCHAR(100));
INSERT INTO #mockup VALUES
(00000,'data0','zz','top','vivalasvegas')
,(00100,'NULL','N/A','sex','no')
,(00100,'NULL','N/A','sex','no')
,(00200,'NULL','female','sex','yes')
,(00200,'NULL','female','sex','yes')
,(00300,'NULL','male','sex','yesplease')
,(00300,'NULL','male','sex','yesplease')
,(00400,'data21','M','--','na')
,(00500,'NULL','F','ezig','na');
WITH Numbered AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY NrValue ORDER BY (SELECT NULL)) AS DupNr
,*
FROM #mockup
)
DELETE FROM Numbered
WHERE DupNr>1;
SELECT * FROM #mockup;
This concept is called updateable CTE. The DELETE FROM Numbered ... will affect the underlying table actually...
If the NrValue is not enough to detect a row as duplicate, just add more columns to the PARTITION BY
You don't need update, you want to delete duplicates so why do you want that intermediate step?
Yor code should look like this:
declare #t table (col1 int, col2 int);
insert into #t values
(1, 1), (1, 1),
(1, 2), (1, 2),(1, 2), (1, 2),
(3, 2), (3, 2),(3, 2);
with cte as
(
select *, row_number() over (partition by col1, col2 order by 1/0) rn
from #t
)
delete cte
where rn > 1;
select *
from #t;
Sorry for not posting it in comment (rows limit and code formatting lost there)

Merge tables having different columns (SQL Server)

I have 2 tables with details as follows
Table 1
Name | City | Employee_Id
-----------------
Raj | CA | A2345
Diya | IL | A1234
Max | PL | A2321
Anna | TX | A1222
Luke | DC | A5643
Table 2
Name | City | Employee_Id | Phone | Age
---------------------------------------
Raj | CA | A2345 | 4094 | 25
Diya | IL | A1234 | 4055 | 19
Max | PL | A2321 | 4076 | 23
As you can see, Employee_Id is the common column in both the columns. I want to update all the entries present in table 1 into table 2.
Raj, Divya and Max are already present in Table 2. So it should not create a duplicate entry in table 2 and skip those 3 entries whereas Anna and Luke are not present in table 2. so this should be added as a new row.
The SQL should be able to merge these 2 columns and ignore the rows which are already present. The final table 2 must be similar to this.
Table 2
Name | City | Employee_Id | Phone | Age
---------------------------------------
Raj | CA | A2345 | 4094 | 25
Diya | IL | A1234 | 4055 | 19
Max | PL | A2321 | 4076 | 23
Anna | TX | A1222 | |
Luke | DC | A5643 | |
Is there a way I could achieve this? I am pretty new to SQL, so any inputs would be of great help. I read about merge and update feature but I guess merge is in Transact-SQL. Also read about joins but could not find a way to crack this.
Demo Setup
CREATE TABLE Table1
([Name] varchar(4), [City] varchar(2), [Employee_Id] varchar(5));
INSERT INTO Table1
([Name], [City], [Employee_Id])
VALUES
('Raj', 'FL', 'A2345'),
('Diya', 'IL', 'A1234'),
('Max', 'PL', 'A2321'),
('Anna', 'TX', 'A1222'),
('Luke', 'DC', 'A5643');
CREATE TABLE Table2
([Name] varchar(4), [City] varchar(2), [Employee_Id] varchar(5), [Phone] int, [Age] int);
INSERT INTO Table2
([Name], [City], [Employee_Id], [Phone], [Age])
VALUES
('Raj', 'CA', 'A2345', 4094, 25),
('Diya', 'IL', 'A1234', 4055, 19),
('Max', 'PL', 'A2321', 4076, 23);
MERGE QUERY
MERGE Table2 AS target
USING Table1 AS source
ON (target.[Employee_Id] = source.[Employee_Id])
WHEN MATCHED THEN
UPDATE SET [Name] = source.[Name],
[City] = source.[City]
WHEN NOT MATCHED THEN
INSERT ([Name], [City], [Employee_Id], [Phone], [Age])
VALUES (source.[Name], source.[City], source.[Employee_Id], NULL, NULL);
SELECT *
FROM Table2

Resources