Selecting the amount of users that have no 'thing' assigned to it - sql

I have a simplified table that shows my problem
Name Animal
Bill Dog
Bill Cat
Bill Fish
John Cat
John Fish
Sara Dog
Sara Cat
Mark Fish
I want the number of people that have no dog. I tried this query.
select count(distinct Name) from Table
where Animal <> 'Dog'
But it returns 4 and not the expected 2. What am I doing wrong?

Your query returned the count of names that have any animal other than dog.
select distinct name
from table t1
where not exists
(
select 1 from table t2 where t1.name=t2.name and t2.Animal='Dog'
)

Use not exists :
select count(distinct t.name) as counts
from table t
where not exists (select 1 from table where name = t.name and animal = 'Dog');
For your current query you are filtering single record based on animal name which would not produced desired result as it should be along with name column.

your where condition "Animal <> 'dog'" is filtering the rows not the names.
So try the below query
Select count(distinct Name) from Table where name not in (
select Name from Table where Animal = 'Dog')

Related

SQL for Exclude

I have a table which is a simple lists of ID numbers and NAMES - I am trying to write a SQL which only returns rows where the NAME does not have particular IDs.
This has been stumping me - the query below returns all as they have other IDs from the exclude lists (large range of IDs). How to structure a query where only those who don't have ID 2 or 3 are returned -- i.e. only returns 'bob' for table below.
select * from TABLE where ID not in (2, 3)
ID NAMES
1 bob
1 alice
2 alice
1 dave
2 dave
3 dave
4 dave
Thank you.
One method is group by and having:
select name
from t
group by name
having sum(case when ID in (2, 3) then 1 else 0 end) = 0;
If you want the original ids, you can add listagg(id, ',') within group (order by id) to the select. Or use not exists:
select t.*
from t
where not exists (select 1
from t t2
where t2.name = t.name and
t2.id in (2, 3)
);

How to compare two tables row by row in SQL

I have two temporary tables with single column
Table 1
MaXAge (Contains maximum age of each kind)
-----------
15
15
11
Table 2
KGroup (Contains each kind)
--------------------------------------------------
Cat
Dog
Parrot
AND another Table Pets with fields
PetID Name Kind Gender Age OwnerID
NOW, I want to display name of animals with maximum age group by kind
You can do this without using any temp tables
SELECT name, age
FROM pets p
JOIN (SELECT kind, MAX(age) max_age FROM pets GROUP BY kind) AS sub ON sub.kind = p.kind AND sub.max_age = p.age

Nested result sets as with dynamic names

I'm trying to join the result of two referencing tables to get row values which are referencing different table names, which rows are selectable by their uuid.
my tables look like this:
table entry
table map
table cats
table dogs
nrrefInt
id name mapRef breed
mapRef breed
1 123
123'dogs'
456 'bengal'
123 'sheepdog'
2 456
456 'cats'
888 'birma' 999 'poodle'
3 789
789'dogs'
4 123
refInt of entry is referencing to map. the name of map is the reference to tables in addition with the field id which is also applied on the tables cats/dogs (dynamic tables loading).
// subset 1: list of numbers that needs to be loaded from entry table (1-4)
SELECT DISTINCT refInt FROM entry WHERE nr in (1,2,3,4)
// subset 2: get all names from map that have the same id like refInt from subset1
SELECT name FROM map WHERE id in subset1
// main query: load all rows from table with the given name
// from map table that have the same mapRef value on it
SELECT * FROM (subset2.names) WHERE mapRef IN (subset2.ids)
result should be the rows:
1) 456 bengal
2) 123 sheepdog
I also made a SQLFiddle of it.
Is there a way to combine this to one query?
It's going to look something like:
SqlFiddle
select
sub.nr,
sub.breed
from (
select e.nr, e.refInt,
case
when c.breed is not null then c.breed
when d.breed is not null then d.breed
else null
end as breed
from (
select e.nr, e.refInt, m.name
from entry e
inner join map m on e.refInt = m.id
) e
left join cats c on e.refInt = c.mapRef and e.name = 'cats'
left join dogs d on e.refInt = d.mapref and e.name = 'dogs'
) sub
where sub.breed is not null
This is going to be very poor in performance.
Now the IMO the correct schema would be:
table entry
nr refint
1 123
2 456
3 789
4 124 (duplicate?)
table breed
mapRef breed species
123 sheepdog 1
999 poodle 1
456 bengal 2
888 birma 2
table species
id species
1 dogs
2 cats
This is normalized and has very good performance.
Note how the following query fully achieves the desired result set and fully demonstrates how the tables cats and dogs are truly just partitions of a single entity animals. The schema should be reworked to reflect this new understanding. This query is also efficient because the inclusion test id pushed to the depths of the innermost CTE's, at the level where actual table rows are being read, without relying on the engine to discover this potential optimization (which can be problematic with UNIONs).
with
cats2 as (
select species='cat', mapref, breed
from cats animals
join entry on entry.refint = animals.mapref
where entry.nr in (1,2,3,4)
),
dogs2 as (
select species='dog', mapref, breed
from dogs animals
join entry on entry.refint = animals.mapref
where entry.nr in (1,2,3,4)
),
animals as (
select species, mapref, breed from cats2
union all
select species, mapref, breed from dogs2
)
select species, mapref, breed
from animals
group by species, mapref, breed
This test script:
declare #entry table (nr int, refint int );
declare #map table (id int, name varchar(20) );
declare #cats table (mapRef int, breed varchar(20));
declare #dogs table (mapRef int, breed varchar(20));
insert #entry(nr,refint) values
(1,123)
,(2,456)
,(3,789)
,(4,123);
insert #map(id,name) values
(123,'dogs')
,(456,'cats')
,(789,'dogs');
insert #cats(mapRef,breed) values
(456,'bengal'),(888,'burma');
insert #dogs(mapRef,breed) values
(123,'sheepdog'), (999,'poodle');
with
cats2 as (
select species='cat', mapref, breed
from #cats animals
join #entry entry on entry.refint = animals.mapref
where entry.nr in (1,2,3,4)
),
dogs2 as (
select species='dog', mapref, breed
from #dogs animals
join #entry entry on entry.refint = animals.mapref
where entry.nr in (1,2,3,4)
),
animals as (
select species, mapref, breed from cats2
union all
select species, mapref, breed from dogs2
)
select species, mapref, breed
from animals
group by species, mapref, breed
yields as desired:
species mapref breed
------- ----------- --------------------
cat 456 bengal
dog 123 sheepdog

How can I insert data from two tables into one?

I have three tables , both with the same fields as in the example below:
Table:
dog
-------------
name, date
Table :
cat
-------------
name, date
Table:
animal
-------------
name, date
as I transfer the dog and cat data for animal table ? I tried the select into but could not do it with two tables.
Table value:
CAT
name date
Garfield 2015-08-03
DOG
name date
Spike 2015-08-03
Source:
insert into animal values ((select * from cat,dog))
Expected result
ANIMAL
name date
Garfield 2015-08-03
Spike 2015-08-03
Try this:
insert into animal
select name, date from dog
union all
select name, date from cat
It can be done with the execution of a query ie:Join operation.Table 1, Table 2,table 3. We have the same attribute in all these three tables.Just join the attributes with table1.fieldname JOIN table2.fieldname JOIN table3.fieldname
There is no need to do a JOIN as there is no relationship between them.
Insert Into animal (name, [date])
Select name, [date]
from dog, cat

Doing a SQL insert without having to look up a value in another table first

Imagine I have two tables:
PETS
SPECIES
The SPECIES table has two columns:
ID - Species ID Number
DESCRIPTION - Description of the species (DOG, CAT, TURTLE, etc.)
The PETS table has two columns:
NAME - Name of the pet
SPECIES a link to the ID field of the SPECIES table
If I want a list of all pets named FLUFFY who are cats, I can do the following in a single SQL statement:
select *
from NAME, SPECIES
where PETS.NAME = 'FLUFFY'
and SPECIES.DESCRIPTION = 'CATS'
and PETS.SPECIES = SPECIES.ID
Now, I have a new pet, BOWSER, who's a dog. I want to be able to say something like this:
INSERT INTO PETS
(PETS.NAME, SPECIES.DESCRIPTION)
VALUES
('BOWSER', 'DOG')
Yes, I know I can first find the SPECIES.ID for DOG, then do the insert:
my $species = $sql.execute(select ID from SPECIES where description = 'DOG');
insert into PETS (NAME, SPECIES)
values ('BOWSER', '$species')
But, I want to do the insert with in a single SQL statement much the way I could do my query. Is that possible?
What about:
INSERT INTO Pets(Name, Species)
SELECT 'Bowser' AS Name, ID AS Species
FROM Species
WHERE Description = 'DOG';
You can use a subselect like:
INSERT INTO Pets (
Name,
Species
) VALUES (
'Bowser',
(SELECT Id FROM Species WHERE Description='Dog')
)
INSERT INTO PETS
(
NAME,
SPECIES
)
SELECT
'BOWSER',
ID
FROM
SPECIES
WHERE
DESCRIPTION='DOG'