SELECT DISTINCT HAVING Count unique conditions - sql

I've searched for an answer on this but can't find quite how to get this distinct recordset based on a condition. I have a table with the following sample data:
Type Color Location Supplier
---- ----- -------- --------
Apple Green New York ABC
Apple Green New York XYZ
Apple Green Los Angeles ABC
Apple Red Chicago ABC
Apple Red Chicago XYZ
Apple Red Chicago DEF
Banana Yellow Miami ABC
Banana Yellow Miami DEF
Banana Yellow Miami XYZ
Banana Yellow Atlanta ABC
I'd like to create a query that shows the count of unique locations for each distinct Type+Color where the number of unique locations is more than 1, e.g.
Type Color UniqueLocations
---- ----- --------
Apple Green 2
Banana Yellow 2
Note that {Apple, Red, 1} doesn't appear because there is only 1 location for red apples (Chicago). I think I've got this one (but perhaps there is a simpler method). I'm using:
SELECT Type, Color, Count(Location) FROM
(SELECT DISTINCT Type, Color, Location FROM MyTable)
GROUP BY Type, Color HAVING Count(Location)>1;
How can I create another query that lists the Type, Color, and Location for each distinct Type,Color when the count of unique locations for that Type,Color is greater than 1? The resulting recordset would look like:
Type Color Location
---- ----- --------
Apple Green New York
Apple Green Los Angeles
Banana Yellow Miami
Banana Yellow Atlanta
Note that Apple, Red, Chicago doesn't appear because there is only 1 location for red apples. Thanks!

Use a COUNT(DISTINCT Location) and join against a subquery on Type and Color The GROUP BY and HAVING clauses as you have attempted to use them will do the job.
/* Be sure to use DISTINCT in the outer query to de-dup */
SELECT DISTINCT
MyTable.Type,
MyTable.Color,
Location
FROM
MyTable
INNER JOIN (
/* Joined subquery returns type,color pairs having COUNT(DISTINCT Location) > 1 */
SELECT
Type,
Color,
/* Don't actually need to select this value - it could just be in the HAVING */
COUNT(DISTINCT Location) AS UniqueLocations
FROM
MyTable
GROUP BY Type, Color
/* Note: Some RDBMS won't allow the alias here and you
would have to use the expanded form
HAVING COUNT(DISTINCT Location) > 1
*/
HAVING UniqueLocations > 1
/* JOIN back against the main table on Type, Color */
) subq ON MyTable.Type = subq.Type AND MyTable.Color = subq.Color
Here is a demonstration

You could write your first query as this:
Select Type, Color, Count(Distinct Location) As UniqueLocations
From Table
Group By Type, Color
Having Count(Distinct Location) > 1
(if you're using MySQL you could use the alias UniqueLocations in your having clause, but on many other systems the aliases are not yet available as the having clause is evaluated before the select clause, in this case you have to repeat the count on both clauses).
And for the second one, there are many different ways to write that, this could be one:
Select Distinct Type, Color, Location
From Table
Where
Exists (
Select
*
From
Table Table_1
Where
Table_1.Type = Table.Type
and Table_1.Color = Table.Color
Group By
Type, Color
Having
Count(Distinct Location) > 1
)

Related

SQLite - Assign a sequential number to each range of rows

In my table every color has index, which defines color order inside country, in every country order may be different.
country
color
color_order
other_data
Canada
green
1
Canada
green
1
Canada
green
1
Canada
red
2
Canada
red
2
Canada
yellow
3
Canada
yellow
3
France
red
1
France
blue
2
France
blue
2
After removing one of colors (all 'red' rows), I need to re-number color_order for each country.
Expected result:
country
color
color_order
other_data
Canada
green
1
Canada
green
1
Canada
green
1
Canada
yellow
2
Canada
yellow
2
France
blue
1
France
blue
1
It should be something like nested loop to iterate through country/color, seems query should include:
ROW_NUMBER() OVER (PARTITION BY country ORDER BY color_order)
Any ideas please?
uses a Common Table Expression (CTE) to first select the rows that you want to keep in the table (all rows where the color is not 'red'). The ROW_NUMBER() function is then used to assign a new color_order to each row based on the order of the color_order column within each country (partition). Finally, an UPDATE statement is used to update the color_order values in the original table to the new values computed in the CTE.
WITH cte AS (
SELECT country, color, color_order, other_data,
ROW_NUMBER() OVER (PARTITION BY country ORDER BY color_order) AS new_order
FROM your_table
WHERE color != 'red'
)
UPDATE cte
SET color_order = new_order;

Using a query to return the most frequent value and the count within a group using SQL in MS Access

Say I have a table showing the type of fruit consumed by an individual over a 24 hour period that looks like this:
Name Fruit
Tim Apple
Tim Orange
Tim Orange
Tim Orange
Lisa Peach
Lisa Apple
Lisa Peach
Eric Plum
Eric Orange
Eric Plum
How would I get a table that shows only the most consumed fruit for each person, as well as the number of fruits consumed. In other words, a table that looks like this:
Name Fruit Number
Tim Orange 3
Lisa Peach 2
Eric Plum 2
I tried
SELECT Name, Fruit, Count(Fruit)
FROM table
GROUP BY Name
But that returns an error because Name needs to be in the GROUP BY statement as well. Every other method I've tried returns the counts for ALL values rather than just the maximum values. MAX(COUNT()) doesn't appear to be a valid statement, so I'm not sure what else to do.
This is a pain, but you can do it. Start with your query and then use join:
SELECT n.Name, n.Fruit
FROM (SELECT Name, Fruit, Count(Fruit) as cnt
FROM table as t
GROUP BY Name, Fruit
) as t INNER JOIN
(SELECT Name, max(cnt) as maxcnt
FROM (SELECT Name, Fruit, Count(Fruit) as cnt
FROM table
GROUP BY Name, Fruit
) as t
GROUP BY Name
) as n
ON t.name = n.name and t.cnt = n.maxcnt;

Oracle find common value in two different columns

If I have a structure like this:
CREATE TABLE things (
id,
personA varchar2,
personB varchar2,
attribute ...,
)
And I want to find, for a given attribute, if I have at least 1 common person for all my things, how would I go about it?
So if my data is (and it could be more than 2 per attribute):
1, John, Steve, Apple
2, Steve, Larry, Apple
3, Paul, Larry, Orange
4, Paul, Larry, Orange
5, Chris, Michael, Tomato
6, Steve, Larry, Tomato
For Apple, Steve is my common person, For Orange both Paul and Larry are, and for Tomato I have no common people. I don't need a query that returns all of these at once, however. I have one of these attributes and want 0, 1, or 2 rows depending on what kind of commonality I have. I've been trying to come up with something but can't quite figure out.
This will give you your common person / attribute list. I ran it against your sample data and got the expected result. Hope it's at least pointing in the right direction :)
WITH NormNames AS (
SELECT PersonA AS Person, Attribute FROM things
UNION ALL SELECT PersonB AS Person, Attribute FROM things
)
SELECT
Person, Attribute, COUNT(*)
FROM NormNames
GROUP BY Person, Attribute
HAVING COUNT(*) >= 2
If you're on 11gR2 you could also use the unpivot operator to avoid the self-join:
select person, attribute
from (
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'))
)
group by person, attribute
having count(*) > 1;
PERSON ATTRIBUTE
---------- ----------
Steve Apple
Paul Orange
Larry Orange
3 rows selected.
Or to just the the people who match the attribute, which I think is what the end of your question is looking for:
select person
from (
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'))
)
where attribute = 'Apple'
group by person, attribute
having count(*) > 1;
PERSON
----------
Steve
1 row selected.
The unpivot translates columns into rows. Run on its own it transforms your original six rows into twelve, replacing the original persona/personb columns with a single person and an additional column indicating which column the new row was formed from, which we don't really care about here:
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'));
ID ATTRIBUTE W PERSON
---------- ---------- - ----------
1 Apple A John
1 Apple B Steve
2 Apple A Steve
2 Apple B Larry
3 Orange A Paul
3 Orange B Larry
4 Orange A Paul
4 Orange B Larry
5 Tomato A Chris
5 Tomato B Michael
6 Tomato A Steve
6 Tomato B Larry
12 rows selected.
The outer query is then doing a simple group.
Here's one method.
It implements an unpivot method by cross-joining to a list of numbers (you could use the unpivot method Alex uses) and then joins the result set, hopefully with a hash join for added goodness.
with
row_generator as (
select 1 counter from dual union all
select 2 counter from dual),
data_generator as (
select
attribute,
id ,
case counter
when 1 then persona
when 2 then personb
end person
from
things,
row_generator)
select
t1.attribute,
t1.person
from
row_generator t1,
row_generator t2
where
t1.attribute = t2.attribute and
t1.person = t2.person and
t1.id != t2.id;

MS Access 2007: Select Unique Records

I have a table with 3 columns. I need to return all records where the records are unique in the third column. I realize this has been well documented all over, but I'm struggling.
Table: cars
Make Model Color
---- ----- -----
Ford Escort Red
Ford Taurus Black
Chevy Vista Green
The query needs to return:
Make Model Color
---- ----- -----
Ford Escort Red
Chevy Vista Green
The, i.e. one of the Ford records.
This is what I've written but it does not work...
SELECT [cars].*
FROM [cars]
LEFT JOIN (SELECT DISTINCT [cars].[Make] FROM [cars) b
ON [cars].[Make] = b.[Make];
Any help is appreciated, thanks.
SELECT a.*
FROM [cars] a
INNER JOIN (SELECT [cars].[Color] FROM [cars] GROUP BY [cars].[Color] HAVING COUNT(*)=1) b
ON a.[Color] = b.[Color];

Parallel SQL queries with aligned data, in MS Access

I have three tables, fruit, person and vegetable
Person
personID personName
-------- ----------
1 Ken
Fruit
personID fruitname
-------- -----
1 apple
1 orange
Vegetable
personID vegetableName
-------- -------------
1 carrot
1 tomato
1 potato
And I want the output to be like this...
personName fruitName vegetableName
---------- --------- -------------
1 apple carrot
1 orange tomato
1 potato
It lessen the duplication of outputs.. is this even possible? can when I tried it before
the values keep repeating? Is their a way to avoid it?
This is only possible if you also add a "position" column to the fruit and vegetables tables, and use this as an additional join column.
Records are not sorted in SQL. So, if you want the sorted, you ALWAYS need a sort criteria, because the SQL standard does not enforce any kind of sort order else.
If an alphabetical sort order is enought, you could dynamically add a position column by something like this (will not work in MS ACCESS, but something similar will do):
SELECT f1.personid, f1.fruitname, count(*) as position
FROM fruit f1 outer join fruit f2 on f1.fruitname = f2.fruitname
and f1.personid = f2.personid
WHERE f2.fruitname < f1.fruitname
GROUP BY f1.personid, f1.fruitname
This query has a position, starting from 0, with the names "before".
Now you can do something like:
select f.personid, f.fruitname, v.vegetablename
from (*fruitquery*) f outer join (*vegetaryquery*) v on f.personid = v.personid
and f.positionid = v.positionid;