SQL - group by occurrence and return id - sql

I have a table of IDs and value:
ID Value
X 1
X 1
X 2
Y 5
Y 5
Y 5
Z 3
Z 6
I want to see which ID contains more than 1 different value. In this case return ID X and Y because X contains[1,2] and Z contains [3,6]:
ID
X
Z
I have tried this:
select ID from
(
SELECT ID
,count(*) over (partition by [Value]) as c
FROM mytable
) a
where c>1
But this is not returning the desired answer

I prefer aggregating this way:
SELECT ID
FROM mytable
GROUP BY ID
HAVING MIN(Value) <> MAX(Value);
On many databases, the above HAVING clause will be sargable, meaning that an index on (ID, Value) can be used. The version which checks COUNT(DISTINCT Value) may not be able to use such an index.

Try this,
SELECT ID
FROM mytable
GROUP BY ID
HAVING COUNT(DISTINCT Value) > 1;

Just group them by ID and check wheter it got more than 1 occurrencies in Value field. Something like this
SELECT ID
FROM table
GROUP BY ID
HAVING COUNT(DISTINCT Value) > 1

CREATE TABLE yourtable(
ID VARCHAR(30) NOT NULL
,Value int NOT NULL
);
INSERT INTO yourtable
(ID,Value) VALUES
('X',1),
('X',1),
('X',2),
('Y',5),
('Y',5),
('Y',5),
('Z',3),
('Z',6);
Other approaches are far better,but I used Rank and Subquery to distinguish ID with more than one occurrence.
SELECT ID
FROM   (SELECT *,
               Rank()
                 OVER(
                   partition BY ID
                   ORDER BY Value) ID2
        FROM   yourtable) a
WHERE ID2 > 1
dbfiddle

Related

Sqlite query - How I can select a predecessor?

I have this table below:
ID name Last
0 Joe Doe
1 Hut Nob
2 Lis Hug
3 Edy mur
I use this query to select an ID:
SELECT name FROM myDatabase WHERE ID = 2
In this case the query returns me the string Lis, now, How I can select the predecessor value?
Simple, the predecessor from 2 is 1, so I need only to do WHERE ID < 2 or WHERE ID = 2 - 1.
But this method have a problem! Lets suppose that I delete that row (ID = 1), the query will return null, because that ID not exists.
So, in this example, how I can select the predecessor from ID 2 and return ID 0? (ID 1 is gone)
You can use subquery to find max ID that is lower than ID that you provided:
SELECT *
FROM mytable m
WHERE m.id = (SELECT MAX(m2.id)
FROM mytable m2
WHERE m2.ID < 2);
SqlFiddleDemo
Get all the smaller IDs, and from those, take only the largest one:
SELECT name
FROM MyTable
WHERE ID < 2
ORDER BY ID DESC
LIMIT 1;

SQL - Select from column A based on values in column B

Lets say I have a table with 2 columns (a, b) with following values:
a b
--- ---
1 5
1 NULL
2 NULL
2 NULL
3 NULL
My desired output:
a
---
2
3
I want to select only those distinct values from column a for which every single occurrence of this value has NULL in column b. Therefore from my desired output, "1" won't come in because there is a "5" in column b even though there is a NULL for the 2nd occurrence of "1".
How can I do this using a TSQL query?
If I understand correctly, you can do this with group by and having:
select a
from t
group by a
having count(b) = 0;
When you use count() with a column name, it counts the number of non-NULL values. Hence, if all values are NULL, then the value will be zero.
It's fairly simple to do:
SELECT A
FROM table1
GROUP BY A
HAVING COUNT(B) = 0
Grouping by A results in all the rows where the value of A is identical to be transferred into a single row in the output. Adding the HAVING clause enables to filter those grouped rows with an aggregate function. COUNT doesn't count NULL values, so when it's 0, there are no other values in B.
Two more ways to do this:
SELECT a
FROM t
EXCEPT
SELECT a
FROM t
WHERE b IS NOT NULL ;
This would use an index on (a, b):
SELECT a
FROM t
GROUP BY a
WHERE MIN(b) IS NOT NULL ;
Try it like this:
DECLARE #tbl TABLE(a INT, b INT);
INSERT INTO #tbl VALUES(1,5),(1,NULL),(2,NULL),(2,NULL),(3,NULL);
--Your test data
SELECT * FROM #tbl;
--And this is what you want - hopefully...
SELECT DISTINCT tbl.a
FROM #tbl AS tbl
WHERE NOT EXISTS(SELECT * FROM #tbl AS x WHERE x.a=tbl.a AND b IS NOT NULL)
To turn your question on it's head, you want the values from column a where there are no non-null values for that value in column b.
select distinct a
from table1 as t1
where 0 = (select count(*)
from table1 as t2
where t1.a = t2.a
and b is not null)
Sample fiddle is here: http://sqlfiddle.com/#!6/5d1b8/1
This should do it:
SELECT DISTINCT a
FROM t
WHERE b IS NULL
AND a NOT IN (SELECT a FROM t WHERE b IS NOT NULL);

Percentage by group - oracle

I have this sample.
What I need is getting an average per key not key and value. However, the syntax I used appear to give me the average per key and value.
select avg(value2),KEY,VALUE from testavg
GROUP BY key,value
order by key, value
Doing otherwise will yield a syntax error. The results I need are as follow:
10 A 0.96
10 B 0.04
12 C 1
But the statement I used yields the incorrect results above.
Could this be achieved by issuing 1 single oracle select statement? I have included the statement to create the entire table.
CREATE TABLE "TESTAVG"
( "KEY" NUMBER,
"VALUE" VARCHAR2(20 BYTE),
"VALUE2" NUMBER
)
Insert into TESTAVG (KEY,VALUE,VALUE2) values (10,'A',12);
Insert into TESTAVG (KEY,VALUE,VALUE2) values (10,'A',13);
Insert into TESTAVG (KEY,VALUE,VALUE2) values (10,'B',1);
Insert into TESTAVG (KEY,VALUE,VALUE2) values (12,'C',20);
This query might run faster on larger data - only reads the table once:
select distinct key, value,
sum(value2) over (partition by key, value) / sum(value2) over (partition by key) r
from testavg
/
KEY VALUE R
---------- -------------------- ----------
10 A .961538462
10 B .038461538
12 C 1
select avg(value2),KEY from testavg
GROUP BY key
order by key;
8.66666666666666666666666666666666666667 10
20 12
EDIT: Specs are still not clear but this might be what you need...
with gr1 as (select key,sum(value2) sumvalue
from testavg
group by key)
, gr2 as (select key,value,sum(value2) sumvalue
from testavg
GROUP BY key,value)
select gr1.key,gr2.value,gr2.sumvalue/gr1.sumvalue
from gr1
, gr2
where gr1.key = gr2.key;
10 B 0.0384615384615384615384615384615384615385
12 C 1
10 A 0.9615384615384615384615384615384615384615

How do I SELECT such that a group by column has every value from supplied list in another coumn

In MS SQL Server 2008, have a table like the following:
CREATE TABLE SomeTable
(
MajorID int NOT NULL REFERENCES ...,
MinorID int NOT NULL,
Value int NOT NULL REFERENCES ...,
PRIMARY KEY(MajorID, MinorID)
)
I also have a set of (Value0, Value1, ...). The goal is to find all such MajorID that have every Value from the set listed at least once. MinorID is not important in this task. The size of set is not predefined, and the set is generated in client application. There's a reasonable limit on its maximum size, say, 64.
What SQL should I use?
Example:
MajorID MinorID Value
1 0 4
1 1 1
1 2 3
1 3 4
1 4 4
1 5 5
1 6 5
2 0 1
3 0 1
3 1 4
For value list (1, 4) the answer is (1, 3), because MajorID 1 and 3 have each value listed at least once.
You can do this with aggregation, as in this query:
select majorid
from t
group by majorid
having COUNT(distinct value) = (select COUNT(distinct value) from t)
The having clause checks that all values are there for a majorid.
This is for all values. If you have a value list, then try this:
with valuelist as (
select 1 as vslue union all
select 4
)
select majorid
from t join
valuelist vl
on t.value = vl.value
group by majorid
having count(distinct value) = (select count(*) from valuelist)
This is the simple way:
select majorid
from ReportStack
where value in (1, 3)
group by majorid
having count (distinct value) = 2
The only maintenance issue with this query is making sure the having clause value (2 in this query) is the same as size of the target value list.

change "many-to-many" to "one-to-many"

I have following table and data:
create table Foo
(
id int not null,
hid int not null,
value int not null
)
insert into Foo(id, hid, value) values(1,1,1) -- use this as 1 < 3
insert into Foo(id, hid, value) values(1,2,3)
insert into Foo(id, hid, value) values(2,3,3) -- use this as 3 < 5
insert into Foo(id, hid, value) values(2,4,5)
insert into Foo(id, hid, value) values(3,2,2) -- use this or next one as value are the same
insert into Foo(id, hid, value) values(3,3,2)
Currently the "id" and "hid" has many-to-many association, what I want to achieve is to make the "hid" as "one" instead of "many", the rule is to use the minimum "value" in the table, see comment in above sql code.
Is this possible use some query to achieve this instead of a cursor?
Thanks!
SQL 2005:
WITH X AS ( SELECT id, min(value) as minval from Foo group by id )
SELECT * FROM
(
SELECT Foo.*, RANK() OVER ( PARTITION by Foo.id order by Foo.hid, Foo.value ) as Rank
FROM Foo JOIN X on Foo.id = X.id and Foo.value = X.minval
) tmp
WHERE Rank = 1
id hid value Rank
----------- ----------- ----------- --------------------
1 1 1 1
2 3 3 1
3 2 2 1
The first line (WITH clause) gets a set of ids with the min value (my arbitrary choice).
The RANK is used to eliminate duplicates - there may be a better way.
With MySql or SQL 2000 I guess you could do this with a complicated set of subqueries.
Not sure if you are looking for a query, or instructions on how to modify your schema, but here is a query:
select id, min(hid) as hid, min(value) as value
from Foo
group by id