I have a SQL problem I am trying to digest. I am using SQL Server 2005.
In a table I have data as such:
ID Type
1 A
2 A
3 A
3 B
4 B
I need to find all of the IDs that have a Type of both A and B.
Use the INTERSECT operator:
SELECT DISTINCT ID FROM [Table] WHERE Type = 'A'
INTERSECT
SELECT DISTINCT ID FROM [Table] WHERE Type = 'B'
select distinct a.id
from table a
join table b on a.id=b.id
where a.type='A'
and b.type='B';
With a semi-join (no sorting, only index seek on B):
select a.id from table a
where a.type = 'A'
and exists (select * from table b where a.id = b.id and b.type = 'B')
If you want to abstract the problem a little bit and find cases where rows with the same id contain different values in the type column, you can check for <> like this:
DECLARE #TestTable TABLE (thisid int, thisval varchar(1))
INSERT INTO #TestTable VALUES (1, 'A')
INSERT INTO #TestTable VALUES (2, 'A')
INSERT INTO #TestTable VALUES (3, 'A')
INSERT INTO #TestTable VALUES (3, 'B')
INSERT INTO #TestTable VALUES (4, 'B')
SELECT DISTINCT thisid
FROM #TestTable a
WHERE EXISTS
( SELECT *
FROM #TestTable b
WHERE a.thisid=b.thisid AND a.thisval<>b.thisval)
-- www.caliberwebgroup.com
This returns:
3
select id, count(type = 'A') as a_count, count(type = 'B') as b_count
from your_table
group by 1
having a_count > 0 and b_count > 0;
At least, this works in sane SQL environments. Dunno if it works in yours.
I was not looking at other answers, but still posting. lol
SELECT distinct t1.ID
FROM table1 AS t1
WHERE exists
(select t2.ID from table1 t2 where t2.type="A" and t2.ID=t1.ID)
and exists
(select t3.ID from table1 t3 where t3.type="B" and t3.ID=t1.ID);
SELECT Id FROM tableX AS x, tableX AS y
WHERE x.id = y.id AND x.type = 'A' AND y.type = 'B'
This is very simple
Declare #t table([ID] INT, [Type] VARCHAR(2))
INSERT INTO #t SELECT 1, 'A' UNION ALL SELECT 2,'A' UNION ALL SELECT 3,'A'
UNION ALL SELECT 3,'B' UNION ALL SELECT 4,'B' UNION ALL SELECT 5,'A' UNION ALL SELECT 5,'A'
;WITH CTE AS
(
SELECT Rn = Row_NUMBER() OVER(PARTITION BY [ID],[TYPE] ORDER BY [ID])
,*
FROM #t
)
SELECT ID
FROM CTE
WHERE Rn =1 AND ([Type]='A' or [Type]='B')
GROUP BY [ID]
HAVING (COUNT([ID])>1)
Output:
id
3
this would help if there are "unknown" amounts of types and you want to find all IDs which have all of types
select id from yourtable group by id having count(*)=(select count(distinct type) from yourtable)
select id
from idtypedata
group by id
having
sum(
case type
when 'A' then 1
when 'B' then 2
-- when 'C' then 4
-- when 'D' then 8
end
) & 1 = 1
And
sum(
case type
when 'A' then 1
when 'B' then 2
-- when 'C' then 4
-- when 'D' then 8
end
) & 2 = 2
Related
I have a result like followed by
ID Name Status
1 A Y
2 A N
3 B Y
4 B Y
5 C N
in this case if status of Name A have two status then I need a select query for following outout
ID Name Status
1 A N
2 A N
3 B Y
4 B Y
5 C N
And sorry, I dont know how ask question for this scenario..
please provide the solution thanks in advance
This following script will select data as per your requirement-
SELECT yt.ID,
yt.Name,
CASE WHEN A.N>1 THEN 'N' ELSE Status END as Status
FROM your_table yt
LEFT JOIN (
SELECT Name,
COUNT(DISTINCT Status) as N
FROM your_table
GROUP BY Name
HAVING COUNT(DISTINCT Status) >1
) A on yt.Name = A.Name
Using LEFT JOIN with COALESCE in the SELECT will work in this case.
Demo with sample data:
DECLARE #TestTable TABLE (ID INT, [Name] VARCHAR (1), [Status] VARCHAR (1));
INSERT INTO #TestTable(ID, [Name], [Status]) VALUES
(1, 'A', 'Y'),
(2, 'A', 'N'),
(3, 'B', 'Y'),
(4, 'B', 'Y'),
(5, 'C', 'N');
SELECT T.ID,
COALESCE(Q.[Name], T.[Name]) AS [Name],
COALESCE(Q.[Status], T.[Status]) AS [Status]
FROM #TestTable T
LEFT JOIN (
SELECT DISTINCT [Name], 'N' AS [Status]
FROM #TestTable
WHERE [Status] = 'N'
) AS Q ON Q.[Name] = T.[Name]
Output:
ID Name Status
1 A N
2 A N
3 B Y
4 B Y
5 C N
Use a RANK in separate query to get the status for the latest id and left join on name against that query to use latest status for all rows for a name
SELECT a.id, a.name, b.status
FROM dbo.Table_3 a
LEFT JOIN (SELECT id, name, status, RANK() OVER (Partition BY name ORDER BY id desc) AS rnk
FROM dbo.table_3) b ON a.name = b.name AND b.rnk = 1
You can use a Windowed function so that you don't need to scan the table twice:
SELECT ID,
[Name],
CASE COUNT(CASE WHEN [Status] = 'N' THEN 1 END) OVER (PARTITION BY [Name]) WHEN 0 THEN [Status] ELSE 'N' END AS [Status]
FROM (VALUES(1,'A','Y'),
(2,'A','N'),
(3,'B','Y'),
(4,'B','Y'),
(5,'C','N')) V(ID, [Name], [Status]);
In below query the derived table a pulls the distinct record that has 'N' . Then joined it with main table and using case statement pulled the status.
Using Derived Table
select *,
case when a.name is not null then 'N' else #temp.status end [status]
from #temp
Left join (select distinct name from #temp where status ='N' )a on a.name = #temp.name
Using Case Statement
select *,
case (select count(*) from #temp t where status='N' and t.Name = #temp.Name)
when 1 then 'N'
else status
end [status]
from #temp
OR
select *,
case when (select count(*) from #temp t where status='N' and t.Name = #temp.Name) > 0 then 'N'
else status
end [status]
from #temp
Output
ID Name Status name status
1 A Y A N
2 A N A N
3 B Y NULL Y
4 B Y NULL Y
5 C N C N
For your particular example, you can just use a window function:
select ID, Name,
min(Status) over (partition by name) as status
from t;
This works because 'N' is less than 'Y', so the MIN() will return 'N' if any values are 'N'.
I want to generate a new column from a table that contains the values 0 or 1. These values are to indicate whether another column contains a value that occurs more than once in the database.
For example:
Table :
Attribute 1
A
B
C
A
B
F
A
B
Attribute New
1
1
0
1
1
0
1
1
and so on... I have already tried it with CASE and HAVING, but somehow I can't find the desired solution. Anybody got any ideas?
Thanks a lot for your help :)
Group BY and COUNT on subquery and Self join by count which is bigger then one,then you can use CASE Expression on select clause.
You can try like this.
SELECT T1.val,CASE WHEN T2.val IS NULL THEN 0 ELSE 1 END as [New]
FROM
T AS T1
LEFT JOIN
(
SELECT val,COUNT(1) 'totle'
FROM T
GROUP BY val
) AS T2 ON T2.totle > 1 and t1.val = t2.val
http://sqlfiddle.com/#!18/ca346/1
I think the DECODE like should work:
Select a1,
CASE PAttribute_1
WHEN (SELECT COUNT(1) FROM T WHERE a1 = p.a1 ) > 1 THEN 1
ELSE 0
END
You can use many attributes to get the required output
sample data:
declare #table table (attribute1 varchar(3))
insert #table (attribute1)
select 'A' union all
select 'B' union all
select 'C' union all
select 'A' union all
select 'B' union all
select 'F' union all
select 'A' union all
select 'B'
;
query:
with cte as
(
select attribute1, max(rn) rn
from (
select attribute1, ROW_NUMBER() over (partition by attribute1 order by attribute1) rn
from #table ) x
group by attribute1
) select c.attribute1, case when c.rn>1 then 1 else 0 end Attribute_new
from #table t inner join cte c on t.attribute1 = c.attribute1
If you are not bothered about the order, you can use something like this:
CREATE TABLE T(val varchar(10));
INSERT INTO T VALUES
('A'),
('B'),
('C'),
('A'),
('B'),
('F'),
('A'),
('B');
select *, case when (count(val) over(partition by val)-1) = 0 then 0 else 1 end OneOrZero
from T
Below is my data.
with cte as(
select 'A' name, 0 status
union all select 'A' name, 1 status
union all select 'B' name, 1 status
union all select 'C' name, 2 status
union all select 'D' name, 1 status
)
I want to get only B, C, D as output from the query. Lets say, 0 is status-complete & I want to ignore records associated with it.
This I am able to achieve using the not in clause as below.
select * from cte c
where c.name not in (select cf.name from cte cf where cf.status=0)
But I want to achieve this using exists or not exists clause in where condition.
Could you please share the logic ?
thanks,
Can you please try with this:
SELECT * FROM cte c
WHERE NOT EXISTS (SELECT cf.name
FROM cte cf WHERE c.name = cf.name AND cf.status = 0)
For this we don't need any column in the where clause because we are addressing that conditional column as comparison in WHERE of sub query.
Please try this
with cte as(
select 'A' name, 0 status
union all select 'A' name, 1 status
union all select 'B' name, 1 status
union all select 'C' name, 2 status
union all select 'D' name, 1 status
)
Select * from cte c
where NOT EXISTS (select 1 from cte cf where cf.status=0 AND c.name = cf.name)
With NOT EXISTS
with cte as(
select 'A' name, 0 status
union all select 'A' name, 1 status
union all select 'B' name, 1 status
union all select 'C' name, 2 status
union all select 'D' name, 1 status
)
select * from cte out where NOT EXISTS
(select inn.name from cte inn WHERE out.name = inn.name and inn.status=0)
DECLARE #tbl1 AS TABLE
(
Name VARCHAR(50),
Status INT
)
INSERT INTO #tbl1 VALUES('A',0)
INSERT INTO #tbl1 VALUES('A',1)
INSERT INTO #tbl1 VALUES('B',1)
INSERT INTO #tbl1 VALUES('C',1)
INSERT INTO #tbl1 VALUES('D',1)
INSERT INTO #tbl1 VALUES('E',0)
With Not EXISTS:
SELECT
*
FROM #tbl1 T1
WHERE NOT EXISTS( SELECT T2.Name FROM #tbl1 T2 WHERE T2.Status=0 AND T1.Name=T2.Name)
With EXISTS:
SELECT
*
FROM #tbl1 T1
WHERE EXISTS( SELECT T2.Name FROM #tbl1 T2 WHERE T1.Name=T2.Name AND T1.Status=1 GROUP BY T2.Name having count(T2.Status)=1 )
Output:
When two sets are given
s1 ={ a,b,c,d} s2={b,c,d,a}
(i.e)
TableA
Item
a
b
c
d
TableB
Item
b
c
d
a
How to write Sql query to display "Elements in tableA and tableB are equal". [Without using SP or UDF]
Output
Elements in TableA and TableB contains identical sets
Use:
SELECT CASE
WHEN COUNT(*) = (SELECT COUNT(*) FROM a)
AND COUNT(*) = (SELECT COUNT(*) FROM b) THEN 'Elements in TableA and TableB contains identical sets'
ELSE 'TableA and TableB do NOT contain identical sets'
END
FROM (SELECT a.col
FROM a
INTERSECT
SELECT b.col
FROM b) x
Test with:
WITH a AS (
SELECT 'a' AS col
UNION ALL
SELECT 'b'
UNION ALL
SELECT 'c'
UNION ALL
SELECT 'd'),
b AS (
SELECT 'b' AS col
UNION ALL
SELECT 'c'
UNION ALL
SELECT 'd'
UNION ALL
SELECT 'a')
SELECT CASE
WHEN COUNT(*) = (SELECT COUNT(*) FROM a)
AND COUNT(*) = (SELECT COUNT(*) FROM b) THEN 'yes'
ELSE 'no'
END
FROM (SELECT a.col
FROM a
INTERSECT
SELECT b.col
FROM b) x
Something like this, using FULL JOIN:
SELECT
CASE
WHEN EXISTS (
SELECT * FROM s1 FULL JOIN s2 ON s1.Item = s2.Item
WHERE s1.Item IS NULL OR s2.Item IS NULL
)
THEN 'Elements in tableA and tableB are not equal'
ELSE 'Elements in tableA and tableB are equal'
END
This has the virtue of short-circuiting on the first non-match, unlike other solutions that require 2 full scans of each table (once for the COUNT(*), once for the JOIN/INTERSECT).
Estimated cost is significantly less than other solutions.
Watch out, I'm gonna use a Cross Join.
Declare #t1 table(val varchar(20))
Declare #t2 table(val varchar(20))
insert into #t1 values ('a')
insert into #t1 values ('b')
insert into #t1 values ('c')
insert into #t1 values ('d')
insert into #t2 values ('c')
insert into #t2 values ('d')
insert into #t2 values ('b')
insert into #t2 values ('a')
select
case when
count(1) =
(((Select count(1) from #t1)
+ (Select count(1) from #t2)) / 2.0)
then 1 else 0 end as SetsMatch from
#t1 t1 cross join #t2 t2
where t1.val = t2.val
My monstrocity:
;with SetA as
(select 'a' c union
select 'b' union
select 'c')
, SetB as
(select 'b' c union
select 'c' union
select 'a' union
select 'd'
)
select case (select count(*) from (
select * from SetA except select * from SetB
union
select * from SetB except select * from SetA
)t)
when 0 then 'Equal' else 'NotEqual' end 'Equality'
Could do it with EXCEPT and a case
select
case
when count (1)=0
then 'Elements in TableA and TableB contains identical sets'
else 'Nope' end from (
select item from s1
EXCEPT
select item from s2
) b
Since this thread was very helpful to me, I thought I'd share my solution.
I had a similar problem, perhaps more generally applicable than this specific single-set comparison. I was trying to find the id of an element that had a set of multi-element child elements that matched a query set of multi-element items.
The relevant schema information is:
table events, pk id
table solutions, pk id, fk event_id -> events
table solution_sources, fk solutionid -> solutions
columns unitsourceid, alpha
Query: find the solution for event with id 110 that has the set of solution_sources that match the set of (unitsourceid, alpha) in ss_tmp. (This can also be done without the tmp table, I believe.)
Solution:
with solutionids as (
select y.solutionid from (
select ss.solutionid, count(ss.solutionid) x
from solutions s, solution_sources ss
where s.event_id = 110 and ss.solutionid = s.id
group by ss.solutionid
) y where y.x = ( select count(*) from ss_tmp )
)
select solutionids.solutionid from solutionids where
(
select case
when count(*) = ( select count(*) from ss_tmp ) then true
else false
end
from
( SELECT unitsourceid, alpha FROM solution_sources
where solutionid = solutionids.solutionid
INTERSECT
SELECT unitsourceid, alpha FROM ss_tmp ) x
)
Tested against a test query of 4 items and a test db that had a matching solution (same number of child elements, each that matched), several completely non-matching solutions, and 1 solution that had 3 matching child elements, 1 solution that had all 4 matching child elements, plus an additional child, and 1 solution that had 4 child elements of which 3 of the 4 matched the query. Only the id of the true match was returned.
thanks a lot
-Linus
Use EXCEPT statement
When using the EXCEPT statement to test if two sets contain the same rows, you will need to do the EXCEPT in both directions (A EXCEPT B and B EXCEPT A). If either comparison returns any records, then the sets are different. If no records are returned by either, they are the same.
The nice thing about this is that you can do this comparison with any number of specific columns and NULL values are handled implicitly without having to jump through hoops to compare them.
A good use case for this is verifying that saving a set of records happened correctly, especially when affecting an existing set.
SELECT IsMatching = (1 ^ convert(bit, count(*)))
FROM (
SELECT Mismatched = 1 -- Can be any column name
FROM (
SELECT Item -- Can have additional columns
FROM TableA
EXCEPT
SELECT Item -- Can have additional columns
FROM TableB
) as A
UNION
SELECT Mismatched = 1 -- Can be any column name
FROM (
SELECT Item -- Can have additional columns
FROM TableB
EXCEPT
SELECT Item -- Can have additional columns
FROM TableA
) as A
) as A
Someone please change my title to better reflect what I am trying to ask.
I have a table like
Table (id, value, value_type, data)
ID is NOT unique. There is no unique key.
value_type has two possible values, let's say A and B.
Type B is better than A, but often not available.
For each id if any records with value_type B exists, I want all the records with that id and value_type B.
If no record for that id with value_Type B exists I want all records with that id and value_type A.
Notice that if B exists for that id I don't want records with type A.
I currently do this with a series of temp tables. Is there a single select statement (sub queries OK) that can do the job?
Thanks so much!
Additional details:
SQL Server 2005
RANK, rather than ROW_NUMBER, because you want ties (those with the same B value) to have the same rank value:
WITH summary AS (
SELECT t.*,
RANK() OVER (PARTITION BY t.id
ORDER BY t.value_type DESC) AS rank
FROM TABLE t
WHERE t.value_type IN ('A', 'B'))
SELECT s.id,
s.value,
s.value_type,
s.data
FROM summary s
WHERE s.rank = 1
Non CTE version:
SELECT s.id,
s.value,
s.value_type,
s.data
FROM (SELECT t.*,
RANK() OVER (PARTITION BY t.id
ORDER BY t.value_type DESC) AS rank
FROM TABLE t
WHERE t.value_type IN ('A', 'B')) s
WHERE s.rank = 1
WITH test AS (
SELECT 1 AS id, 'B' AS value_type
UNION ALL
SELECT 1, 'B'
UNION ALL
SELECT 1, 'A'
UNION ALL
SELECT 2, 'A'
UNION ALL
SELECT 2, 'A'),
summary AS (
SELECT t.*,
RANK() OVER (PARTITION BY t.id
ORDER BY t.value_type DESC) AS rank
FROM test t)
SELECT *
FROM summary
WHERE rank = 1
I get:
id value_type rank
----------------------
1 B 1
1 B 1
2 A 1
2 A 1
SELECT *
FROM table
WHERE value_type = B
UNION ALL
SELECT *
FROM table
WHERE ID not in (SELECT distinct id
FROM table
WHERE value_type = B)
The shortest query to do the job I can think of:
SELECT TOP 1 WITH TIES *
FROM #test
ORDER BY Rank() OVER (PARTITION BY id ORDER BY value_type DESC)
This is about 50% worse on CPU as OMG Ponies' and Christoperous 5000's solutions, but the same number of reads. It's the extra sort that is making it take more CPU.
The best-performing original query I've come up with so far is:
SELECT *
FROM #test
WHERE value_type = 'B'
UNION ALL
SELECT *
FROM #test T1
WHERE NOT EXISTS (
SELECT *
FROM #test T2
WHERE
T1.id = T2.id
AND T2.value_type = 'B'
)
This consistently beats all the others presented on CPU by about 1/3rd (the others are about 50% more) but has 3x the number of reads. The duration on this query is often 2/3rds the time of all the others. I consider it a good contender.
Indexes and data types could change everything.
declare #test as table(
id int , value [nvarchar](255),value_type [nvarchar](255),data int)
INSERT INTO #test
SELECT 1, 'X', 'A',1 UNION
SELECT 1, 'X', 'A',2 UNION
SELECT 1, 'X', 'A',3 UNION
SELECT 1, 'X', 'A',4 UNION
SELECT 2, 'X', 'A',5 UNION
SELECT 2, 'X', 'B',6 UNION
SELECT 2, 'X', 'B',7 UNION
SELECT 2, 'X', 'A',8 UNION
SELECT 2, 'X', 'A',9
SELECT * FROM #test x
INNER JOIN
(SELECT id, MAX(value_type) as value_type FROM
#test GROUP BY id) as y
ON x.id = y.id AND x.value_type = y.value_type
Try this (MSSQL).
Select id, value_typeB, null
from myTable
where value_typeB is not null
Union All
Select id, null, value_typeA
from myTable
where value_typeB is null and value_typeA is not null
Perhaps something like this:
select * from mytable
where id in (select distinct id where value_type = "B")
union
select * from mytable
where id in (select distinct id where value_type = "A"
and id not in (select distinct id where value_type = "B"))
This uses a union, combining all records of value B with all records that have only A values:
SELECT *
FROM mainTable
WHERE value_type = B
GROUP BY value_type UNION SELECT *
FROM mainTable
WHERE value_type = A
AND id NOT IN(SELECT *
FROM mainTable
WHERE value_type = B);