How to count the number of distinct values for each specific - sql

I have a database containing two separate fields A and B. I want to find out if for any given value of A there are multiple rows with different values of B.
I have tried using group by and distinct but I am doing something wrong, because I keep getting results which, when I query the specific value of A, all the values of B are the same. I have tried variants on the following including:
SELECT COUNT(B) FROM ex1 GROUP BY A HAVING COUNT(*) > 1;
SELECT COUNT(DISTINCT B) FROM ex1 GROUP BY A HAVING COUNT(DISTINCT B) > 1;
Strangely, this last one wound up giving me results where for a given value of B there were multiple values of A, which is backwards from what I wanted. I tried reversing A and B in the last query but that wound up giving me cases where A only had a single value of B.
How can I get records for only where there is a specific value of A in multiple records, each of which has a different value for B?

Give this a try:
"records for only where there is a specific value of A in multiple records, each of which has a different value for B?"
SELECT DISTINCT ex1a.A
FROM ex1 ex1a
WHERE
(SELECT COUNT(ex1b.B) FROM ex1 ex1b WHERE ex1a.A=ex1b.A)
= (SELECT COUNT(DISTINCT ex1b.B) FROM ex1 ex1b WHERE ex1a.A=ex1b.A)
AND
(SELECT COUNT(ex1c.B) FROM ex1 ex1c WHERE ex1a.A = ex1c.A) > 1
And, you can remove the last SELECT if you want to include the case where there is just 1 (distinct) record for A and B.

this should work:
create table want as
select a,b,count(*)as cnt from(
select a,b,count(*) as num from have
group by a, b)
group by a having cnt > 1;

Related

Count Distinct values in one column based on other column

I am trying to count distinct values on Z_l based on value by using with clause. Sample data exercise included below.
please look at the picture, the distinct values of Z_l based on X='ny'
with distincz_l as (select ny.X, ny.z_l o.cnt From HOPL ny join (select X, count(*) as cnt from HOPL group by X) o on (ny.X = o.Z_l)) select * from HOPL;
You don't even need a WITH clause, since you just need one single sentence:
SELECT z_l, count(1)
FROM hopl
WHERE x='ny'
GROUP BY z_l
;

How can I find out the relationship between two columns in database?

I have a view defined in SQL Server database and it has two columns A and B, both of which have the type of INT. I want to find out the relationship between these two, 1 to 1 or 1 to many or many to many. Is there a SQL statement I can use to find out?
For the relationship, it means for a given value of A, how many values of B maps to this value. If there is only one value, then it is 1 to 1 mapping.
You could use CTEs to generate COUNTs of how many distinct A values were associated with each B value and vice versa, then take the MAX of those values to determine if the relationship is 1 or many on each side. For example:
WITH CTEA AS (
SELECT COUNT(DISTINCT B) ac
FROM t
GROUP BY A
),
CTEB AS (
SELECT COUNT(DISTINCT A) bc
FROM t
GROUP BY B
)
SELECT CONCAT(
CASE WHEN MAX(bc) = 1 THEN '1' ELSE 'many' END,
' to ',
CASE WHEN MAX(ac) = 1 THEN '1' ELSE 'many' END
) AS [A to B]
FROM CTEA
CROSS JOIN CTEB
Note that any time a relationship is listed as 1, it may actually be many but just not showing that because of limited data in the table.
Demo on dbfiddle
Assuming you have no NULL values:
select (case when count(*) = count(distinct a) and
count(*) = count(distinct b)
then '1-1'
when count(*) = count(distinct a) or
count(*) = count(distinct b)
then '1-many'
else 'many-many'
end)
from t;
Note: This does not distinguish between 1-many for a-->b or b-->a.
You would use count and group by to get this information.
--This would give you count of values of b which map to every values of a. If there is at least one row with a count give you a value greater than 1 it means the mapping between a and b is one to many.
select a,count( distinct b)
from table
group by a
If all of the rows have the values equal to one for all of the elements in a then the mapping is one-one
A caveat , null in b would be ignored in count expressions. ie because null and another null is not equivalent

Efficient way to verify a table is a subset of another table

I have two tables A and B, the structures are exactly the same. I need to verify A is a subset of B. Because the structure contains over 100 fields, I do not want to list them one by one in a where predicates.
I would like to know if there is any more easier way to do that
Assumptions:
(1) Identical table structure of A and B. This means that both order of columns and their data types have to match.
(2) There are no duplicate rows in table A
Problem description
To prove that A is a subset of B you need to show that A\B = empty set.
Solution
This means that if you remove every row in A that has a matching row in B and your output is empty (0 rows) this means that A is subset of B.
If on the other hand, in the output you get > 0 rows it means that A has rows that B doesnt and that A isn't a subset of B.
SELECT * FROM A
EXCEPT
SELECT * FROM B
When A is empty (contains 0 rows) it will be treated as a subset of B, because the result of above query will be 0 rows.
#robertoplancarte's approach with little tweaking
with tB_cnt as
(
SELECT COUNT(*) cnt FROM
(
SELECT DISTINCT * FROM dbo.T_B
) T_B
), TAB_cnt as
(
SELECT count(*) cnt FROM
(
SELECT * FROM dto.T_B
UNION
SELECT * FROM dto.T_A
) T_AB
)
SELECT
CASE WHEN TB_CNT.CNT = TAB_CNT.CNT THEN
'Table A is subset of B'
else
'Table A is not subset of B'
END as Result
FROM TAB_CNT, TB_CNT

SQL Oracle - query to return rows based on data matchng rules

I have the below data
NUMBER SEQUENCE_NUMBER
CA00000045 AAD508
CA00000045 AAD508
CA00000046 AAD509
CA00000047 AAD510
CA00000047 AAD510
CA00000047 AAD511
CA00000048 AAD511
and I would like to find out which rows do not match the following rule:
NUMBER will always be the same when the SEQUENCE_NUMBER is the same.
So in the above data 'AAD508' will mean the NUMBER value will be the same on each row where the same value appears in the SEQUENCE_NUMBER.
I want to right a query that will bring me back rows where this rule is
broken. So for example:
CA00000047 AAD511
CA00000048 AAD511
I don't know where to start with this one, so have no initial SQL i'm afraid.
Thanks
You want to self join on the data to compare each row to all others sharing the same sequence number, and then filter using a with statement to only get rows with non-matching numbers. You did not give a name for the table so I added it as "table_name" below
SELECT
a.NUMBER,
a.SEQUENCE_NUMBER
FROM table_name a
INNER JOIN table_name b
ON a.SEQUENCE_NUMBER = b.SEQUENCE_NUMBER
WHERE a.NUMBER <> b.NUMBER
GROUP BY 1,2
Threw in the group by to act as a distinct
I would simply use exists:
select t.*
from t
where exists (select 1
from t t2
where t2.sequence_number = t.sequence_number and
t2.number <> t.number
);
If sequence_numbers() only had up to two rows, you could get each rule-breaker on one row:
select sequence_number, min(number), max(number)
from t
group by sequence_number
having min(number) <> max(number);
Or, you could generalize this to get the list of numbers on a single row:
select sequence_number, listagg(number, ',') within group (order by number) as numbers
from t
group by sequence_number
having min(number) <> max(number);

Find Rows where the Same Two Column Values Recur

Given a table in SQL-Server like:
Id INTEGER
A VARCHAR(50)
B VARCHAR(50)
-- Some other columns
with no index on A or B, I wish to find rows where a unique combination of A and B occurs more than once.
I'm using the query
SELECT A+B, Count(A+B) FROM MyTable
GROUP BY A+B
HAVING COUNT(A+B) > 1
First Question
Is there a more time-efficient way to do this? (I cannot add indices to the database)
Second Question
When I attempt to gain some formatting of the output by including a , in the concatenation:
SELECT A+','+B, Count(A+','+B) FROM MyTable
GROUP BY A+','+B
HAVING COUNT(A+','+B) > 1
The query fails with the error
Column 'MyDB.dbo.MyTable.A' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
with a similar error for Column B.
How can I format the output to separate the two columns?
It would seem more natural to me to write:
SELECT A, B, Count(*) FROM MyTable
GROUP BY A, B
HAVING COUNT(*) > 1
And it's the most efficient way of doing it (and so is the query in the question).
Similarly to the above query, you can rewrite your second query:
SELECT A + ',' + B, Count(*) FROM MyTable
GROUP BY A, B
HAVING COUNT(*) > 1