SQL select Distinct with where clause - sql

I have i table like this.
PersonID, KvalifikationId
1 1
1 2
1 3
2 1
2 3
I want to write SQL querye returning all persons, that have not kvalifikation 2.
i Wrote
SELECT DISTINCT PersonID where NOT KvalifikationID = 2
But this return both person 1 and person 2.
How do i make select that only return personId's that not have kval2 ?

Try this,
SELECT DISTINCT PersonID
FROM tableName
WHERE PersonID NOT IN
(
SELECT PersonID
FROM tableName
WHERE KvalifikationId = 2
)
SQLFiddle Demo

Declare #t table(PersonID int,KvalifikationId int)
Insert Into #t Select 1 ,1
Insert Into #t Select 1, 2
Insert Into #t Select 1,3
Insert Into #t Select 2 ,1
Insert Into #t Select 2,3
Select PersonId From #t
Except
Select PersonID From #t where KvalifikationId = 2
Result
PersonId
2

By using your Person table rather than your N:N table in the outer query you can skip the distinct and the anti semi join to the sub query will have better performance since it is on a clustered index. (assuming PersonID is pk in the Person table)
SELECT PersonID
FROM tblPerson
WHERE NOT EXISTS
(
SELECT NULL
FROM tblPersonKvalifikation
WHERE KvalifikationId = 2 AND
tblPerson.PersonID = tblPersonKvalifikation.PersonID
)

SELECT DISTINCT person_id
FROM tableName t1
WHERE not exists
(
select 1
from tableName
where person_id = t1.person_id and KvalifikationId = 2
)

try this.
SELECT DISTINCT PersonID from tableName
WHERE KvalifikationId NOT IN ('2');

Related

How to get last record from Master-Details tables

I have a table that has 3 columns.
create table myTable
(
ID int Primary key,
Detail_ID int references myTable(ID) null, -- reference to self
Master_Value varchar(50) -- references to master table
)
this table has the follow records:
insert into myTable select 100,null,'aaaa'
insert into myTable select 101,100,'aaaa'
insert into myTable select 102,101,'aaaa'
insert into myTable select 103,102,'aaaa' ---> last record
insert into myTable select 200,null,'bbbb'
insert into myTable select 201,200,'bbbb'
insert into myTable select 202,201,'bbbb' ---> last record
the records is saved In the form of relational with ID and Detail_ID columns.
I need to select the last record each Master_Value column. follow output:
lastRecordID Master_Value Path
202 bbbb 200=>201=>202
103 aaaa 100=>101=>102=>103
tips:
The records are not listed in order in the table.
I can not use the max(ID) keyword. beacuse data is not sorted.(may
be the id column updated manually.)
attempts:
I was able to Prepare follow query and is working well:
with Q as
(
select ID ,Detail_ID, Master_Value , 1 RowOrder, CAST(id as varchar(max)) [Path] from myTable where Detail_ID is null
union all
select R.id,R.Detail_ID , r.Master_Value , (q.RowOrder + 1) RowOrder , (q.[Path]+'=>'+CAST(r.id as varchar(max))) [Path] from myTable R inner join Q ON Q.ID=R.Detail_ID --where r.Dom_ID_RowType=1010
)
select * into #q from Q
select Master_Value, MAX(RowOrder) lastRecord into #temp from #Q group by Master_Value
select
q.ID lastRecordID,
q.Master_Value,
q.[Path]
from #temp t
join #q q on q.RowOrder = t.lastRecord
where
q.Master_Value = t.Master_Value
but I need to simple way (one select) and optimal method.
Can anyone help me?
One method uses a correlated subquery to get the last value (which is how I interpreted your question):
select t.*
from mytable t
where not exists (select 1
from mytable t2
where t2.master_value = t.master_value and
t2.id = t.detail_id
);
This returns rows that are not referred to by another row.
For the path, you need a recursive CTE:
with cte as (
select master_value, id as first_id, id as child_id, convert(varchar(max), id) as path, 1 as lev
from mytable t
where detail_id is null
union all
select cte.master_value, cte.first_id, t.id, concat(path, '->', t.id), lev + 1
from cte join
mytable t
on t.detail_id = cte.child_id and t.master_value = cte.master_value
)
select cte.*
from (select cte.*, max(lev) over (partition by master_value) as max_lev
from cte
) cte
where max_lev = lev
Here is a db<>fiddle.

SQL Server : SELECT ID having only a single condition

I have a patients table with details such as conditions that the patient has. from the below table I want to select Patients, Claims which have ONLY a single condition - 'Hypertension'. Example Patient B is the expected output. Patient A will not be selected because he claimed for multiple conditions.
+----+---------+--------------+
| ID | ClaimID | Condition |
+----+---------+--------------+
| A | 14234 | Hypertension |
| A | 14234 | Diabetes |
| A | 63947 | Diabetes |
| B | 23853 | Hypertension |
+----+---------+--------------+
I tried using the NOT IN condition as below but doesn't seem to help
SELECT ID, ClaimID, Condition
FROM myTable
WHERE Condition IN ('Hypertension')
AND Condition NOT IN ('Diabetes')
One method uses not exists:
select t.*
from mytable t
where t.condition = 'Hypertension' and
not exists (select 1
from mytable t2
where t2.id = t.id and t2.condition <> t.condition
);
Or you can do it like this:
select
id,
claim_id,
condition
from
patient
where
id in
(
select
id
from
patient
group by
id having count (distinct condition) = 1
);
Result:
id claim_id condition
-- ----------- ----------------
B 23853 Hypertension
(1 rows affected)
Setup:
create table patient
(
id varchar(1),
claim_id int,
condition varchar(16)
);
insert into patient (id, claim_id, condition) values ('A', 14234, 'Hypertension');
insert into patient (id, claim_id, condition) values ('A', 14234, 'Diabetes');
insert into patient (id, claim_id, condition) values ('A', 63947, 'Diabetes');
insert into patient (id, claim_id, condition) values ('B', 23853, 'Hypertension');
You can do this with a CTE.
I set up this CTE with two parameters, one being the Condition you seek, and the other being the max number of combined conditions to find (in your case 1).
DECLARE #myTable TABLE (Id VARCHAR(1), ClaimID INT, Condition VARCHAR(100))
INSERT INTO #myTable (Id, ClaimID, Condition)
SELECT 'A',14234,'Hypertension' UNION ALL
SELECT 'A',14234,'Diabetes' UNION ALL
SELECT 'A',63947,'Diabetes' UNION ALL
SELECT 'B',23853,'Hypertension'
DECLARE #Condition VARCHAR(100)
DECLARE #MaxConditions TINYINT
SET #Condition='Hypertension'
SET #MaxConditions=1
; WITH CTE AS
(
SELECT *, COUNT(2) OVER(PARTITION BY ClaimID) AS CN
FROM #myTable T1
WHERE EXISTS (SELECT 1 FROM #myTable T2 WHERE T1.ClaimID=T2.ClaimID AND T2.Condition=#Condition)
)
SELECT *
FROM CTE
WHERE CN<=#MaxConditions
If you don't care about the fluff, and just want all ClaimID's with just ONE condition regardless of which condition it is use this.
DECLARE #myTable TABLE (Id VARCHAR(1), ClaimID INT, Condition VARCHAR(100))
INSERT INTO #myTable (Id, ClaimID, Condition)
SELECT 'A',14234,'Hypertension' UNION ALL
SELECT 'A',14234,'Diabetes' UNION ALL
SELECT 'A',63947,'Diabetes' UNION ALL
SELECT 'B',23853,'Hypertension'
DECLARE #MaxConditions TINYINT
SET #MaxConditions=1
; WITH CTE AS
(
SELECT *, COUNT(2) OVER(PARTITION BY ClaimID) AS CN
FROM #myTable T1
)
SELECT *
FROM CTE
WHERE CN<=#MaxConditions
Here is one method using Having clause
SELECT t.*
FROM mytable t
WHERE EXISTS (SELECT 1
FROM mytable t2
WHERE t2.id = t.id
HAVING Count(CASE WHEN condition = 'Hypertension' THEN 1 END) > 0
AND Count(CASE WHEN condition != 'Hypertension' THEN 1 END) = 0)
And yet a couple of other ways to do this:
declare #TableA table(Id char,
ClaimId int,
Condition varchar(250));
insert into #TableA (id, claimid, condition)
values ('A', 14234, 'Hypertension'),
('A', 14234, 'Diabetes'),
('A', 63947, 'Diabetes'),
('B', 23853, 'Hypertension')
select id, claimid, condition
from #TableA a
where not exists(select id
from #TableA b
where a.id = b.id
group by b.id
having count(b.id) > 1)
OR
;with cte as
(
select id, claimid, condition
from #TableA
)
,
cte2 as
(
Select id, count(Id) as counts
from cte
group by id
having count(id) < 2
)
Select cte.id, claimid, condition
From cte
inner join
cte2
on cte.id = cte2.id
I decided to revise my answer into an appropriate one.
A simple solution to your question is to count the rows instead of the ID values (since it's not an integer).
Here is a simple introduction:
SELECT
ID
FROM
#PatientTable
GROUP BY
ID
HAVING
ID = ID AND COUNT(*) = 1
This will Return the ID B
+----+
| ID |
+----+
| B |
+----+
Surely, this is not enough, as you may work with a large data and need more filtering.
So, we will go and use it as a sub-query.
Using it as a sub-query it's simple :
SELECT
ID,
ClaimID,
Condition
FROM
#PatientTable
WHERE
ID = (SELECT ID AS NumberOfClaims FROM #PatientTable GROUP BY ID HAVING ID = ID AND COUNT(*) = 1)
This will return
+----+---------+--------------+
| ID | ClaimID | Condition |
+----+---------+--------------+
| B | 23853 | Hypertension |
+----+---------+--------------+
So far so good, but there is another issue we may face. Let's say you have a multiple Claims from a multiple patients, using this query as is will only show one patient. To show all patients we need to use IN rather than = under the WHERE clause
WHERE
ID IN (SELECT ID AS NumberOfClaims FROM #PatientTable GROUP BY ID HAVING ID = ID AND COUNT(*) = 1)
This will list all patients that falls under this condition.
If you need more conditions to filter, you just add them to the WHERE clause and you'll be good to go.
SELECT id, sum(ct)
FROM (SELECT customer_id, CASE WHEN category = 'X' THEN 0 else 1
end ct
FROM MASTER_TABLE
) AS t1
GROUP BY id
HAVING sum(ct) = 0
id which will have sum(ct) more than 1, will have multiple conditions
Use joins instead of subquery. Joins are always better in performance. You can use below query.
SELECT T1.id, T1.claimid, T1.Condition
FROM mytable T1
INNER JOIN
(
select id, count(Condition) counter
from mytable
group by id HAVING COUNT(DISTINCT CONDITION)=1
) T2 ON T1.ID=T2.ID
WHERE T2.counter=1

Can I delete multiple of nth rows in a single query from a table in SQL?

I want to delete multiple of 4 from my table which have thousands of record. How can I do it?
Ex:-
Table1
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
I want to delete every 4th row.
I don't want to use a loop or cursor.
Delete A from
(
Select *,Row_Number() Over(Order By Id) as RN from TableA
) A
where RN%4=0
SQL Fiddle Link
Try this...
delete from table_name where (col1 % 4) = 0
Use a CTE.
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.rowfield) AS rank
FROM Table1 t)
SELECT rowfield, fielda
FROM cte
WHERE rank%4 != 0
Output
rowfield fielda
1 a
2 b
3 c
5 e
6 f
7 g
9 i
SQL Fiddle: http://sqlfiddle.com/#!6/c9540b/13/0
Once you are happy with the output use DELETE FROM.
This can use an index if one exists and uses numbers table
;with cte
as
(select n from numbers
where n%4=0
)
delete t
from table1 t
join
cte c
on c.n=t.id
Try this
DECLARE #nvalToDelete varchar(100)='4,7,3,8,9'-- just give values to delete from table
DECLARE #temp TABLE
(
valtodelete VARCHAR(100)
)
DECLARE #Deletetemp TABLE
(
valtodelete INT
)
INSERT INTO #temp
SELECT #nvalToDelete
INSERT INTO #Deletetemp
SELECT split.a.value('.', 'VARCHAR(1000)') AS valToDelete
FROM (SELECT Cast('<S>' + Replace(valtodelete, ',', '</S><S>')
+ '</S>' AS XML) AS valToDelete
FROM #temp) AS A
CROSS apply valtodelete.nodes('/S') AS Split(a)
DECLARE #Table1 TABLE
(
ID INT,
val varchar(10)
)
INSERT INTO #Table1
SELECT 1,'a' UNION ALL
SELECT 2,'b' UNION ALL
SELECT 3,'c' UNION ALL
SELECT 4,'d' UNION ALL
SELECT 5,'e' UNION ALL
SELECT 6,'f' UNION ALL
SELECT 7,'g' UNION ALL
SELECT 8,'h' UNION ALL
SELECT 9,'i'
SELECT *
FROM #Table1;
WITH cte
AS (SELECT *,
RN = Row_number()
OVER (
ORDER BY id )
FROM #Table1)
DELETE FROM #Table1
WHERE id IN(SELECT id FROM cte
WHERE rn IN (SELECT CASt(valToDelete AS INT) FROM #Deletetemp)
)
SELECT *
FROM #Table1

SQL:How do i get the row that has the max value of a column in SQL Server

I have a data a set which is already grouped by Person and Class columns and I use this query for this process:
SELECT Person,Class, MAX(TimeSpent) as MaxTimeSpent
FROM Persons
GROUP BY Person,Class
Output:
Person Class MaxTimeSpent
--------|--------|-------------|
MJ | 0 | 0 |
MJ | 1 | 659 |
MJ | 2 | 515 |
What I want to do is to get the row that has the maximum Class value in this data set (which is the 3rd row for this example).
How can I do this ? Any help would be appreciated.
Try this one
SELECT T.*
FROM
(SELECT Person,
Class,
MAX(TimeSpent) AS MaxTimeSpent
FROM Persons AS P
WHERE Person = 'MJ'
GROUP BY Person, Class) AS T
WHERE T.class = (
SELECT MAX(class) FROM Persons AS P
WHERE P.person = T.person)
You can use cte for that.
declare #Persons table (person nvarchar(10),Class int ,TimeSpent int)
insert into #Persons
select 'MJ',0,0 union all
select 'MJ',1,659 union all
select 'MJ',2,515
;with cte
as(
SELECT Person,Class,TimeSpent , row_number() over(partition by Person order by Class desc ) as RN
FROM #Persons
)
select * from cte where RN=1
Solution 2 :With Out Cte:
SELECT * FROM (
SELECT Person
,Class
,TimeSpent
,row_number() OVER (PARTITION BY Person ORDER BY Class DESC) AS RN FROM #Persons
) t WHERE t.RN = 1
TRY This:
declare #t table (Person varchar (20),Class int ,MaxTimeSpent int )
insert into #t VALUES ('MJ',0,0)
insert into #t VALUES ('MJ',1,659)
insert into #t VALUES ('MJ',2,515)
SELECT TOP 1 * FROM #t ORDER BY 2 DESC
--OR
SELECT * FROM #t WHERE Class = (SELECT max(class) FROM #t)
-- OR
SELECT TOP 1 * FROM (
SELECT *
,ROW_NUMBER() OVER (PARTITION BY person ORDER BY Class DESC) Record_Count FROM #t
) a
SELECT Top 1 Person,Class, MAX(TimeSpent) as MaxTimeSpent
FROM Persons
GROUP BY Person,Class order by Class desc

How to remove common fields in two tables

I have two tables , table 1 and table 2.
The fields of table 1 are :
book,pen,pencil,bag
The fields of table 2 are :
car,van,book,bike,pencil
When I run the query I want the query to ignore the duplicate or common fields and return the other field.
The output should be as follows,
car,van,bike,pen,bag
Perhaps:
SELECT x.thing FROM
(
SELECT thing FROM dbo.Table1
UNION ALL
SELECT thing FROM dbo.Table2
) X
GROUP BY x.thing
Having Count(*) = 1
Demo
However, this will also remove items that are duplicates in their table which might or might not be desired.
Have you tried sth like this:
delete form X
where (car =
Select distinct car
from X
where x);
distinct--> return the differents values.
try this one:
declare #table1 table (col1 varchar(max))
declare #table2 table (col1 varchar(max))
insert into #table1 values
('book'),('pen'),('pencil'),('bag')
insert into #table2 values ('car'),('van'),('book'),('bike'),('pencil')
;with cte
as (
select COUNT(1) as total_item, col1 from (
select col1 from #table1
union all
select col1 from #table2
)a group by col1
)
select col1 from cte where total_item = 1
WITH uniontables AS (
SELECT NULL AS car,
NULL AS van,
book,
NULL AS bike,
pen,
pencil,
bag
FROM [Table 1 ]
UNION
SELECT car,
van,
book,
bike,
NULL AS pen,
pencil,
NULL AS bag
FROM [Table 2 ] )
SELECT DISTINCT * FROM uniontables