SQL Query problems exist - sql

I'm having a lot of troubles with the last query I need and I think it's a level out of my league so any help is appreciated.
The tables:
CREATE TABLE Recipe
(
nrecipe integer,
name varchar(255),
primary key (nrecipe)
);
CREATE TABLE Food
(
designation varchar(255) unique,
quantity integer,
primary key (designation)
);
CREATE TABLE Contains
(
nrecipe integer,
designation varchar(255),
quantity integer,
primary key (nrecipe, designation),
foreign key (nrecipe) references Recepie (nrecipe),
foreign key (designation) references Food (designation)
);
Quantity in Food table is the quantity stored in warehouse.
Quantity in Contains is the amount needed of a food element to use in recipe.
Quantity in Food table and Contains differ from each other.
The query:
I want to know the names of ALL recipes that are possible to be done with the food stored in warehouse.
It requires that the quantity of every element of food in warehouse is bigger than the quantity needed for the recipe.
EDIT: also, it shouldn't show a recipe's name if there is nothing referring to it on Contains table.
To make it easier to understand, I'll give some data:
INSERT INTO Recipe VALUES ('01', 'Steak with potatos and water');
INSERT INTO Recipe VALUES ('02', 'Rice and ice tea');
INSERT INTO Recipe VALUES ('03', 'Potatos and shrimp');
INSERT INTO Recipe VALUES ('04', 'Water');
INSERT INTO Recipe VALUES ('05', 'Steak with rice');
INSERT INTO Recipe VALUES ('06', 'Steak with spaguetti');
INSERT INTO Recipe VALUES ('07', 'Potatos with rice');
INSERT INTO Food VALUES ('Water', 5);
INSERT INTO Food VALUES ('Ice tea', 10);
INSERT INTO Food VALUES ('Steak', 30);
INSERT INTO Food VALUES ('Potatos', 20);
INSERT INTO Food VALUES ('Rice', 50);
INSERT INTO Food VALUES ('Shrimp', 5);
INSERT INTO Food VALUES ('Spaguetti', 5);
INSERT INTO Contains VALUES ('01', 'Steak', 1);
INSERT INTO Contains VALUES ('01', 'Potatos', 15);
INSERT INTO Contains VALUES ('01', 'Water', 10);
INSERT INTO Contains VALUES ('02', 'Rice', 5);
INSERT INTO Contains VALUES ('02', 'Ice tea', 8);
INSERT INTO Contains VALUES ('03', 'Potatos', 1);
INSERT INTO Contains VALUES ('03', 'Shrimp', 10);
INSERT INTO Contains VALUES ('04', 'Water', 20);
INSERT INTO Contains VALUES ('05', 'Steak', 1);
INSERT INTO Contains VALUES ('05', 'Rice', 20);
INSERT INTO Contains VALUES ('06', 'Steak', 1);
INSERT INTO Contains VALUES ('06', 'Spaguetti', 10);
The outcome expected from the query is:
Rice and ice tea
Steak with rice
Since it's the only two recipes with enough quantity in warehouse.
EDIT: potatoes with rice shouldn't appear as it is a recipe but isn't in contains list
Thanks for input and time. Any help is welcome :)

I'd use >= ALL operator :
SELECT name
FROM Recipe R
WHERE 0 >= ALL (SELECT C.quantity - F.quantity
FROM Food F
INNER JOIN Contains C
USING (designation)
WHERE C.nrecipe = R.nrecipe);
The correct spelling is recipe, and you used different names for some columns (recepie, nrecipe, nrecepie) so I changed it. Note that instead of using a varchar primary key, you should use a numeric one.
Edit:
SELECT name
FROM Recipe R
WHERE 0 >= ALL (SELECT C.quantity - F.quantity
FROM Food F
INNER JOIN Contains C
USING (designation)
WHERE C.nrecipe = R.nrecipe)
AND EXISTS(SELECT NULL
FROM Contains C
WHERE C.nrecipe = R.nrecipe);

This is in SQL Server because that is what I have:
select
r.name
from
Recepie r
where
not exists
(
select 1
from
[Contains] c
where
c.nrecipe = r.nrecepie and
not exists
(
select 1
from
Food f
where
f.designation = c.designation and
f.quantity >= c.quantity
)
)
Which in plain language is "Get me all recipes where there are no ingredients of insufficient quantity"

Related

Need to get value from a lookup table

I have a table-X with ecode,emp ID ( some values)
37,10
47,20
57,30
There are 2 lookup tables
lookup table 1 has just the emp ID details( which am interested in)
10
20
so when i join..i get all the values needed (thats one part)
my result will be
37 10
47 20
second part is,
the ones which doesnt satisfy the join condition should lookup on table 2 which has
2 columns
ecode, other_codes
37 xxx
47 YYY
57 AAA
So when 30 comes in , i want to return AAA and my final dataset should be,
37, 10
47,20
57 AAA
appreciate any help!
Thanks
You can left join both tables and use CASE statement for selecting a value from one table or the other.
I've created a db-fiddle which I think exemplifies your situation based on your description: https://www.db-fiddle.com/f/ujW8Unf44CqbsiJZXqXXtK/0
Here is the code for posterity. To set up your tables:
CREATE TABLE tableX (
eCode int,
employeeId int
);
INSERT INTO tableX (eCode, employeeId) VALUES (37, 10);
INSERT INTO tableX (eCode, employeeId) VALUES (47, 20);
INSERT INTO tableX (eCode, employeeId) VALUES (57, 30);
CREATE TABLE employeeIds (employeeId int);
INSERT INTO employeeIds (employeeId) VALUES (10);
INSERT INTO employeeIds (employeeId) VALUES (20);
CREATE TABLE otherCodes (
eCode int,
other_codes varchar(10)
);
INSERT INTO otherCodes (eCode, other_codes) VALUES (37, 'XXX');
INSERT INTO otherCodes (eCode, other_codes) VALUES (47, 'YYY');
INSERT INTO otherCodes (eCode, other_codes) VALUES (57, 'AAA');
The query based on this schema:
SELECT
tx.eCode,
CASE WHEN ei.employeeId IS NULL THEN oc.other_codes ELSE ei.employeeId END as 'result'
FROM tableX tx
LEFT JOIN employeeIds ei ON tx.employeeId = ei.employeeId
LEFT JOIN otherCodes oc ON tx.eCode = oc.eCode;

postgres aggregate join matches to multiple array fields while creating views

I have the following schema + data:
create table org_users (
id character varying (255),
settings_id character varying (255) -- fk: settings.id
);
create table settings (
id character varying (255), -- primary key settings_id
perdiem_settings character varying (255), -- jsonised fk to perdiems.id
floor_settings character varying (255) -- jsonised fk to floors.id
);
create table perdiems (
id integer, -- primary key
name character varying(255)
);
create table floors (
id integer, -- primary key
name character varying (255)
);
insert into perdiems (id, name) values (1, 'perdiem 1');
insert into perdiems (id, name) values (2, 'perdiem 2');
insert into perdiems (id, name) values (3, 'perdiem 3');
insert into floors (id, name) values (1, 'floor 1');
insert into floors (id, name) values (2, 'floor 2');
insert into floors (id, name) values (3, 'floor 3');
insert into settings (id, perdiem_settings, floor_settings) values ('setting1', '{"allowed_per_diem_ids":[1, 2]}', '{"allowed_floor_ids":[1]}');
insert into settings (id, perdiem_settings, floor_settings) values ('setting2', '{"allowed_per_diem_ids":[2, 3]}', '{"allowed_floor_ids":[1, 2]}');
insert into settings (id, perdiem_settings, floor_settings) values ('setting3', '{"allowed_per_diem_ids":[3, 1]}', '{"allowed_floor_ids":[1, 2, 3]}');
insert into org_users (id, settings_id) values ('user1', 'setting1');
insert into org_users (id, settings_id) values ('user2', 'setting2');
insert into org_users (id, settings_id) values ('user3', 'setting3');
Now I want to create a view which will have aggregates from each of the other table, into an array field of its own. To explain with an example, the view that I want should be like:
org_user_id | settings_id | perdiems | floors
--------------------------------------------------------------------------------------------
user1 | setting1 | ['perdiem 1', 'perdiem 2'] | ['floor 1']
user2 | setting2 | ['perdiem 2', 'perdiem 3'] | ['floor 1', 'floor 2']
user3 | setting3 | ['perdiem 3', 'perdiem 1'] | ['floor 1', 'floor 2', 'floor 3']
This question is somewhat related to postgres aggregate join matches to an array field which deals with creating array fields out of join matches. However, here I want to create multiple array fields in a single view and so using a GROUP BY clause will not be feasible iiuc.
The query that I tried is:
CREATE OR REPLACE VIEW users_settings_view AS
SELECT ou.id AS org_user_id, <other fields...>
FROM org_users ou
LEFT JOIN settings pdr_s ON pdr_s.id = ou.settings_id
LEFT JOIN perdiems pdr ON pdr.id = ANY (SELECT json_array_elements(perdiem_settings::JSON->'allowed_per_diem_ids')::text::int FROM settings)
which creates duplicate records for each of the matching perdiem because of the join and not creating an array. Even if I crate an array as mentioned in the other stackoverflow question, it won't work if I have multiple string arrays as part of the view for different columns. Any way I can get multiple join matches to multiple array fields in a single view ?
This will give you the result.
select ou.id, array_agg( DISTINCT pd.name ),
array_agg( DISTINCT f.name )
from org_users ou join settings s on ou.settings_id = s.id
cross join lateral
json_array_elements_text(((s.perdiem_settings)::json->'allowed_per_diem_ids')::json)
as jp(perdiem) join
perdiems pd
on pd.id = jp.perdiem::int
cross join lateral
json_array_elements_text(((s.floor_settings)::json->'allowed_floor_ids')::json)
as js(floor) join
floors f
on f.id = js.floor::int
GROUP BY ou.id;
Demo
Edit
For cases of NULL settings, you may use a separate UNION ALL
select id , ARRAY[NULL] as perdiems ,ARRAY[NULL] as floors FROM org_users
WHERE settings_id IS NULL
UNION ALL
(
-- The above query --
) ORDER BY id;
Demo2

Passing a parameter value for like

create table ter (
ID number,
category varchar2(250 byte),
name varchar2(250 byte)
);
insert into ter values (1, 'amd-visual theraphy','john');
insert into ter values (1, 'amd-visual theraphy','mike');
insert into ter values (2, 'amd-autmatic theraphy','mike');
insert into ter values (3, 'amd-autmatic theraphy','drane');
insert into ter values (3, 'cmd autmatic theraphy', 'traverse');
insert into ter values (3, 'amd-dramatic theraphy','drane');
insert into ter values (3, 'cmd-dropertic theraphy', 'traverse');
insert into ter values (5,'qwd-dropertic visual-theraphy','drones');
insert into ter values (5,'qwd-aromatic-theraphy','drones');
insert into ter values (3, 'other', 'traverse');
insert into ter values (3, 'other', 'traverse');
1: &category is null display all records
2: &category is not null and if i enter category =visual,autmatic then display respectively
3 &category is not null and if i enter category =dramatic it should display dramtic and dropertic ignoring other results;
as dramatic and dropertic is the requirement where the user want to see and ignoring the if partial value matches with visual and autmatic contains
how to obtain this result
this is what one gave the solution to me but still i want to ignore the visual containing dropetic
select *
from ter t cross join (select '&category' as my_categ from dual) m
where m.my_categ is null
or m.my_categ in ('visual', 'autmatic', 'dramtic') and t.category like '%'
|| m.my_categ || '%'
or m.my_categ = 'dramtic' and t.category like '%dropertic%' ;
I'd change your data model a bit.
Instead of saving categories as a space separated string, i'd better move them to separate table.
create table category(
ter_id number,
name varchar(250 bytes)
);
where ter_id is the same as ID in your ter column.
This table should contain records like:
insert into category values (1, 'amd-visual');
insert into category values (1, 'therapy');
-- etc.
Implying above your query will be something like:
select t.*, c.*
from ter t, category c
where t.id = c.ter_id
and category in ('therapy', 'visual');
-- etc...
If you really need to satisfy criteria:
if category is null display all records
it's easy to implement just adding
record with empty category name to table category:
insert into category values (1, null);
insert into category values (2, null);
-- etc.

Using the SUM function in SQLITE

I have this basic sqlite3 database. In my store table, I am focused on the numberOfItems category. e.g., in my PurchaseToStore table, 4 different stores buy tacos with the number of them (numberOfItems) of 5, 55, 55, 55. What I want to do is calculate the sum of those values. So (55 + 55 + 55 + 5) = 170.
CREATE TABLE Store(
storeName VARCHAR(30),
storeid INTEGER,
PRIMARY KEY(storeid)
);
CREATE TABLE item(
itemName VARCHAR(30) ,
itemId INTEGER,
PRIMARY KEY(itemId)
);
CREATE TABLE PurchasedToStore(
storeid INTEGER,
itemId INTEGER,
numberOfItems INTEGER,
expirationMonth Integer,
PRIMARY KEY(storeid, itemID),
FOREIGN KEY(storeid) REFERENCES STORE(storeid),
FOREIGN KEY(itemid) REFERENCES item(itemid)
);
INSERT INTO Store Store('Meijer', 1);
INSERT INTO Store VALUES('Kroger', 2);
INSERT INTO Store VALUES('Walmart', 3);
INSERT INTO Store VALUES('Kmart', 4);
INSERT INTO item VALUES('taco', 556);
INSERT INTO item VALUES('burrito', 557);
INSERT INTO item VALUES('cheese', 600);
INSERT INTO STORE VALUES(1, 556, 5, 12);
INSERT INTO STORE VALUES(2, 556, 55, 11);
INSERT INTO STORE VALUES(3, 556, 55, 10);
INSERT INTO STORE VALUES(4, 556, 55, 2);
Here is what I have to try to do this:
SELECT sum(numberOfItems)
FROM item join purchasetostore
WHERE storeid = itemid;
It's a bit unclear what the end goal is, but a slight change to your where condition will get your desired sum. You'll want to match up itemid from the two tables;
SELECT sum(numberOfItems)
FROM item join PurchasedToStore
WHERE item.itemid = PurchasedToStore.itemid;
A probably slightly more useful query could include the item name of the items, and sum per name;
SELECT item.itemname, sum(numberOfItems)
FROM item join PurchasedToStore
WHERE item.itemid = PurchasedToStore.itemid
GROUP BY item.itemname;
A simple SQLfiddle with both queries.

SQL Join tables - detecting presence of some tuples but not others

I've got two primary tables: codes and categories.
I've also got a join table code_mappings which associates codes with categories.
I need to be able to determine which codes are mapped to one group of categories, but not mapped to another. Been banging my head against this for a while, but am completely stuck.
Here's the schema:
create table codes(
id int,
name varchar(256));
create table code_mappings(
id int,
code_id int,
category_id int);
create table categories(
id int,
name varchar(256));
And some seed data:
INSERT INTO categories VALUES(1, 'Dental');
INSERT INTO categories VALUES(2, 'Weight');
INSERT INTO categories VALUES(3, 'Other');
INSERT INTO categories VALUES(4, 'Acme Co');
INSERT INTO categories VALUES(5, 'No Name');
INSERT INTO codes VALUES(100, "big bag of cat food");
INSERT INTO codes VALUES(200, "healthy doggie treatz");
INSERT INTO code_mappings VALUES(50, 200, 1);
INSERT INTO code_mappings VALUES(51, 100, 4);
INSERT INTO code_mappings VALUES(52, 100, 3);
How would I write a query that will give me the codes that are mapped to one of categories (1,2,3) but not to one of categories (4,5)?
This is an example of a set-within-sets query. I like to approach these using group by and having, because I find that the most flexible approach:
select cm.code_id
from code_mappings cm
group by cm.code_id
having sum(case when cm.category_id in (1, 2, 3) then 1 else 0 end) = 1 and
sum(case when cm.category_id in (4, 5) then 1 else 0 end) = 0;
Each condition in the having clause implements exactly one of the conditions. You said one code of 1, 2, or 3, hence the = 1 (if you wanted at least one of these three, it would be > 0). You said no 4 or 5, hence = 0.
SELECT *
FROM codes co
WHERE EXISTS (
SELECT *
FROM code_mappings ex
WHERE ex.code_id = co.id
AND ex.category_id IN (1,2,3)
)
AND NOT EXISTS (
SELECT *
FROM code_mappings nx
WHERE nx.code_id = co.id
AND nx.category_id IN (4,5)
)
;