SQL to return if value is double - sql

I have a question on how to validate if a row's doubled when other columns meet criteria.
The table looks looks like this:
Type Name ID Am
O Name1 1234 1
O Name1 1235 1
O Name1 4569 2
X Name2 1234 1
X Name2 4569 2
C name3 1234 1
For type O, I have under Name1, 2 ID's for the same Am = 1.
I'd like to to do a query that would check if multiples IDs can be found under same type & name & am values and return if >1, but ignore the rest of the types.
Thank you!

Try this
select type, name, am
from table
group by type, name, am
having count(*)>1

most dbms support row_number,so you can use this
select * from
(
select *, row_number() over(partition by Type,Name,Am order by ID) as rn
from t
) t1 where t1.rn>1

Are you trying to check if a combination of type/name has more than one am value?
If so:
select type, name
from t
group by type, name
having min(am) <> max(am);

I would use NOT EXISTS :
SELECT t.*
FROM table t
WHERE EXISTS (SELECT 1
FROM table t1
WHERE t1.Type = t.Type AND t1.Name = t.Name AND t1.Am = t.Am AND t1.ID <> t.ID
);

Related

Trying to look up records based on a join

I'm trying to work on a stored procedure that is somewhat tricky, let's say I have Table_1 with this data:
Num1 Name1 Code1 Desc
-------------------------------------------
123B Apple 10 Text1
123B Apple 11 Text1
123C Google 20 Text2
I also have a lookup table that looks like this:
Tbl_LookUp
Num1 Code1
-------------------
123B 10
123C 25
So what I am trying to do in this scenario is:
Select data from Table_1 WHERE:
There is a match between Table_1 and Tbl_Lookup on Num1
and
If there is a more than 1 record for a particular Num1 in Table_1, then only return the row where Table_1.Code1=Tbl_Lookup.Code1
Otherwise, if there is only 1 record for a particular Num1 in Table_1, then even if the Table_1.Code1 = Tbl_Lookup.Code1 does not work, still return the record.
Desired end result:
Num1 Name1 Code1 Desc
--------------------------------------------
123B Apple 10 Text1
123C Google 20 Text2
123B is returned because there are multiple records for this Num1. One of them has the Code1 that corresponds to Tbl_Lookup.Code1
123C is returned, because although the Code1 does not match Tbl_Lookup, there is only one record, so in that case join doesn't matter, and we still want to return it.
Any help is greatly appreciated.
Not sure if there is a better way to do this. But this should give you want you are looking for
select t.*
from table1 t
join Tbl_LookUp l on l.Num1 = t.Num1
where t.code1 = l.code1
or exists ( select count(1) from table1 i
where i.Num1= t.Num1
group by Num1
having count(Num1) = 1 )
One way is
select t.Num1, t.Name1, t.Code1, t.Desc
from (
select Num1, Name1, Code1, Desc,
count(code1) over(partition by Num1) cnt
from Table_1 ) t
join Tbl_Lookup tl on t.Num1 = tl.Num1
and (t.cnt = 1 or t.Code1 = tl.Code1)
This is a great place to use apply:
select t1.*
from tbl_lookup l cross apply
(select top (1) t1.*
from table1 t1
where t1.num1 = l.num1
order by (case when t.code = l.code1 then 1 else 2 end)
);
Yet another way to obtain the desired results - identify exact lookup matches with exists and count occurences of num1, then allow any with a count of 1 or only matches on both columns where more than 1:
select num1, name1, code1, [desc]
from (
select * , case when exists (select * from [lookup] l where l.num1 = t.num1 and l.code1 = t.code1) then 1 end lmatch, Count(*) over (partition by num1) cnt
from t1 t
where exists (select * from [lookup] l where l.num1 = t.num1)
)x
where lmatch = 1 and cnt > 1 or cnt = 1;

SQL aggregate and filter functions

Consider following table:
Number | Value
1 a
1 b
1 a
2 a
2 a
3 c
4 a
5 d
5 a
I want to choose every row, where the value for one number is the same, so my result should be:
Number | Value
2 a
3 c
4 a
I manage to get the right numbers by using nested
SQL-Statements like below. I am wondering if there is a simpler solution for my problem.
SELECT
a.n,
COUNT(n)
FROM
(
SELECT number n , value k
FROM testtable
GROUP BY number, value
) a
GROUP BY n
HAVING COUNT(n) = 1
You can try this
SELECT NUMBER,MAX(VALUE) AS VALUE FROM TESTTABLE
GROUP BY NUMBER
HAVING MAX(VALUE)=MIN(VALUE)
You can try also this:
SELECT DISTINCT t.number, t.value
FROM testtable t
LEFT JOIN testtable t_other
ON t.number = t_other.number AND t.value <> t_other.value
WHERE t_other.number IS NULL
Another alternative using exists.
select distinct num, val from testtable a
where not exists (
select 1 from testtable b
where a.num = b.num
and a.val <> b.val
)
http://sqlfiddle.com/#!9/dd080dd/5

How do you select all rows when data occurs in one of the rows?

I have a table with data
|FormID|Name|
1 A
1 B
2 A
2 C
3 B
3 C
I am trying to query all rows where Name 'A' appears, however i also want to get all rows with the same FormID when the name occurs
For example
Select * from table where name = 'A'
resultset
|FormID|Name|
1 A
2 A
1 B
2 C
Right now i am just querying for the FormID values where the name occurs and then doing another query with the FormID number (Select * from table where formID in (1,2) ) but there must be a way to do this in one sql statement
You can use exists:
select t.*
from t
where t.name = 'A' or
exists (select 1
from t t2
where t2.formid = t.formid and t2.name = 'A'
);
Actually, the first condition is not necessary, so this suffices:
select t.*
from t
where exists (select 1
from t t2
where t2.formid = t.formid and t2.name = 'A'
);
Another approach:
SELECT formid, name
FROM forms
WHERE formid IN (SELECT formid FROM forms WHERE name = 'A')
ORDER BY name;
gives
formid name
---------- ----------
1 A
2 A
1 B
2 C
Because the subquery in the IN doesn't depend on the current row being looked at, it only has to be evaluated once, making it more potentially more efficient for large tables.

Querying a subset

I want to write an SQL query to find records which contain a particular column and from that subset want to find records which doesn't contain a some other value. How do you write a query for that?
cid id2 attribute
--------------------------------
1 100 delete
1 100 payment
1 100 void
2 100 delete
2 102 payment
2 102 void
3 102 delete
3 103 payment
In above example, I want to list cid for which payment and delete attributes exist but void attribute doesn't exist. So it should list out 3 from above example because it doesn't have void attribute.
Forgot to mention that there could be more attributes. However, I need to list out records for which delete and payment exist regardless of other attributes but void doesn’t.
I call this a "set-within-sets" query, because you are looking for particular sets of attributes within each cid.
I would express this with group by and conditions in the having:
select cid
from t
group by cid
having sum(case when attribute = 'payment' then 1 else 0 end) > 0 and
sum(case when attribute = 'delete' then 1 else 0 end) > 0 and
sum(case when attribute = 'void' then 1 else 0 end) = 0 ;
In some databases, you can simplify this with string aggregation -- assuming there are no duplicate attributes for cids. For instance, using the MySQL function:
select cid
from t
where attribute in ('payment', 'delete' 'void')
group by cid
having group_concat(attribute order by attribute) = 'delete,payment';
You can use conditional aggregation:
select cid
from tablename
where attribute in ('delete', 'payment', 'void')
group by cid
having
count(distinct attribute) = 2
and
sum(
case attribute
when 'void' then 1
else 0
end
) = 0
If there are not more attributes than these 3, then you can omit the WHERE clause.
See the demo.
Results:
| cid |
| --- |
| 3 |
I'm assuming that there are only three attributes, so the logic behind this query is:
First COUNT the number of attributes GROUP BY cid, and then LEFT JOIN the original table ON attribute is void. You should grab cid that has exactly 2 attributes and no void.
The original table is named as temp:
SELECT
subq2.result_cid
FROM (
SELECT
*
FROM (
SELECT
T.cid AS result_cid,
COUNT(T.attribute) AS count
FROM
temp AS T
GROUP BY
T.cid
) AS subq
LEFT OUTER JOIN temp AS T2 ON subq.result_cid = T2.cid AND T2.attribute = 'void'
) AS subq2
WHERE subq2.count = 2 AND subq2.id2 IS NULL
use corelated subquery by using not exists
select t1.* from tablename t1
where not exists( select 1 from tablename t2
where t1.cid=t2.cid and attribute='void'
)
and exists ( select 1 from tablename t2
where t1.cid=t2.cid
having count(distinct attribute)=2
)
and attribute in ('payment','delete')
demo online

Select rows having the same features than others

I've the following table with 3 columns: Id, FeatureName and Value:
Id FeatureName Value
-- ----------- -----
1 AAA 10
1 ABB 12
1 BBB 12
2 AAA 15
2 ABB 12
2 ACD 7
3 AAA 10
3 ABB 12
3 CCC 12
.............
Each Id has different features and each Feature has a value for that Id.
I need to write a query which gives me the Ids that have exactly the same features and values than a given one, but only taking into account those whose name starts with 'A'. For example, in the top table, I can use that query to search for all the Ids that have the same features. For example, features with values where Id=1 would result Id=3 with same features starting with 'A' and same values for these features.
I found a couple of different ways to do this, but all of them go very slow when the table has lots of rows (more than hundred of thousands)
The way I obtain the best performance is using the next query:
select a2.Id
from (select a.FeatureName, a.Value
from Table1 a
where a.Id = 1) a1,
(select a.Id, a.FeatureName, a.Value
from Table1 a
where a.FeatureName like 'A%') a2
where a1.FeatureName = a2.FeatureName
and a1.value = a2.value
group by a2.Id
having count(*) = 2
intersect
select a.Id
from Table1 a
where a.FeatureName like 'A%'
group by a.Id
having count(*)= 2
where #nFeatures is the number of features starting by 'A' in Id=1. I counted them before calling this query. I make the intersection to avoid results that have the same parameters than Id=1 but also some others whose name starts with 'A'.
I think that the slowest part is the second subquery:
select a.Id, a.FeaureName, a.Value
from MyTable a
where a.FeatureName = 'A%'
but I don't know how to make it faster. Maybe I will have to play with the indexes.
Any idea of how could I write a fast query for this purpose?
So you want all rows where the combination of FeatureName and Value is not unique? You can use EXISTS:
SELECT t.*
FROM dbo.Table1 t
WHERE t.FeatureName LIKE 'A%'
AND EXISTS(SELECT 1 FROM dbo.Table1 t2
WHERE t.Id <> t2.ID
AND t.FeatureName = t2.FeatureName
AND t.Value = t2.Value)
Demo
how could I write a fast query for this purpose?
If it's not fast enough create an index on FeatureName + Value.
I tried to eliminate the join with MyTable again to select the data for the ID's that have matching FeatureName and Value values. Here's the query:
with joined_set as
(
SELECT
mt1.*, mt2.id as mt2_id, mt2.featurename as mt2_FeatureName, mt2.value as mt2_value
from
(
select *
from mytable
where featurename like 'A%'
) mt1
left join
(
select *
from mytable
where featurename like 'A%'
) mt2
on mt2.id <> mt1.id and mt2.FeatureName = mt1.featurename and mt2.value = mt1.value
)
select distinct id
from joined_set
where id not in
(select id
from joined_set
group by id
having SUM(
CASE
WHEN mt2_id is null THEN 1
ELSE 0
END
) <> 0
);
Here is the SQL Fiddle demo. It has an extra condition in the inline view mt2, to perform this search only for id = 1.
I'm a little dense this morning, I'm not sure if you wanted just the ID's or...
Here's my take on it...
You could probably move the where FeatureName like 'A%' into the inner query to filter the data on the initial table scan.
with dupFeatures (FeatureName, Value, dupCount)
as
(
select FeatureName, Value, count(*) as dupCount from MyTable
group by FeatureName, Value
having count(*) > 1
)
select MyTable.Id, dupFeatures.FeatureName,dupFeatures.Value
from dupFeatures
join MyTable on (MyTable.FeatureName = dupFeatures.FeatureName and
MyTable.Value = dupFeatures.Value )
where dupFeatures.FeatureName like 'A%'
order by FeatureName, Value, Id
A general solution is
With Rows As (
select id
, FeatureName
, Value
, rows = Count(id) OVER (PARTITION BY id)
FROM test
WHERE FeatureName LIKE 'A%')
SELECT a.id aID, b.id bID
FROM Rows a
INNER JOIN Rows b ON a.id < b.id and a.FeatureName = b.FeatureName
and a.rows = b.rows
GROUP BY a.id, b.id
ORDER BY a.id, b.id
to limit the solution to a group just add a WHERE condition on the main query for a.ID. The CTE is needed to get the correct number of rows for each id
SQLFiddle demo, in the demo I changed little the test data to have a another couple of ID with only one of the FeatureName of 1 and 3