Search code examples
mysqlsqlsql-serversqliterelational-division

find rows that have multiple linked rows


I am working on my highschool project I want to run a query for user to search medicines using multiple categories here is the ddl,

Create table medicine (
  MedId int(11) NOT NULL AUTO_INCREMENT,
  Name varchar(30) COLLATE utf8_bin DEFAULT NULL,
  Vendor varchar(20) COLLATE utf8_bin DEFAULT NULL,
  Quantity int(11) NOT NULL,
  type text COLLATE utf8_bin NOT NULL,
  price int(11) NOT NULL,
  PRIMARY KEY (MedId)
); 

create table category(
 CatId smallint auto_increment,
 Name varchar (20),
 primary key(CatID)
);

create table MedicineCat(
MedId int,
CatId smallint,
    foreign key (MedId) references medicine(MedId),
    foreign key (CatId) references category(CatId),
    primary key(MedId,CatId)


    );


SELECT medicinecat.MedID AS 'MedId', medicine.Name AS 'Name', medicine.price AS 'Price', medicine.Quantity AS 'Quantity'
FROM medicinecat
JOIN medicine ON medicine.MedId = medicinecat.MedId
WHERE CatId in (1,2,3);

What I am getting is all the medicines that has at least one of the category , I know that's because of IN clause, what I want is to select medicine that has "all the categories" not one of the category.


Solution

  • select
        m.MedID AS 'MedId',
        m.Name AS 'Name',
        m.price AS 'Price',
        m.Quantity AS 'Quantity'
    from medicine as m
    where
        exists (
            -- to get only those having all three categories
            select 1
            from medicinecat as mc
            where mc.CatId in (1,2,3) and mc.MedID = m.MedID 
            having count(*) = 3
        )
    

    If you can do some generic solution like

    select
        m.MedID AS 'MedId',
        m.Name AS 'Name',
        m.price AS 'Price',
        m.Quantity AS 'Quantity'
    from medicine as m
    where
        not exists (
            select *
            from (values (1), (2), (3)) as tt(CatId)
                left outer join medicinecat as mc on mc.CatId = tt.CatId and mc.MedID = m.MedID
            where mc.CatId is null
        )
    

    or

    select
        m.MedID AS 'MedId',
        m.Name AS 'Name',
        m.price AS 'Price',
        m.Quantity AS 'Quantity'
    from medicine as m
    where
        exists (
            -- to get only those having all categories
            select 1
            from (values (1), (2), (3)) as tt(CatId)
                left outer join medicinecat as mc on mc.CatId = tt.CatId and mc.MedID = m.MedID
            having count(tt.CatId) = count(mc.CatId)
        )