SQLite select specific product and the rest ordered - sql

I have an issue. Suppose i have the table in Northwind database, where there are orders placed containing some products.
Order | Product
1 | Milk
1 | Cacao
1 | Juice
2 | Milk
2 | LemonJuice
2 | OrangeJuice
3 | Lemonade
3 | Remoulade
3 | GrapefruitJuice
Suppose Order is being placed and it contains FX Order 1 Milk, Cacao, Juice. Order 2 has Milk, LemonJuice, OrangeJuice.
I need to select all the orders which contains Milk ordered and to select the rest what have they ordered, SO If person in order 1 has ordered Milk, then i need to take cacao and juice as well. The same in order 2, I see Milk ordered, then i need to take LemonJuice and OrangeJuice, Whereas Order 3 does not contain Milk, So I do not need it.
How can i do that?
Trying for the second day, I am really in doubt of how to write it..

An embedded select would do it:
Select * from Orders where ord in
(select ord from Orders where Product = "Milk");
One thing I would like to point out is that "Order" is a keyword in SQLite, so you cannot name your column "Order". As you see in my code-snippet I renamed it to "ord". (The Table is called Orders and the second column is called (as in your description) Product.

Related

SQL query to match similar customers

I'm trying to find the query in order to match similar customers.
To simplify the situation consider this scenario:
I have a table which contains a customer name and product purchased.
customer name can have multiple purchases of same and different products.
So firstly I can take distinct customer name and product name, so I see all customers and all products they purchased at least once.
Now I want a query to show me a sort of matching customers, according to the product they both purchased, so I want to count the similar products they purchased.
So I want to see for each pair of customers (pairing all the table) the amount of similar product they purchased.
Lets say the raw data is:
CustomerName | ProductName
A | 1
A | 2
A | 1
A | 3
B | 1
B | 2
B | 4
C | 2
Then I want to see the result of:
CustomerName1 | CustomerName2 | CountSimilarity
A | B | 2
A | C | 1
B | C | 1
And so on for all pairs of customers that have at least 1 similar product purchasing
Any suggestions how to approach this query?
The environment is SQL Server.
Thanks
Here is a self join approach:
SELECT t1.CustomerName, t2.CustomerName, COUNT(*) AS CountSimilarity
FROM yourTable t1
INNER JOIN yourTable t2
ON t1.ProductName = t2.ProductName
WHERE
t1.CustomerName < t2.CustomerName
GROUP BY
t1.CustomerName, t2.CustomerName;
Two records are joined together above if their products match. Note that the inequality in the WHERE clause ensures that customer pairs do not appear in duplicate.

Split a row into multiple rows based on a column value

I am trying to split a record in a table to 2 records based on a column value. The input table displays the 3 types of products and their price. For a specific product (row) only its corresponding column has value. The other columns have Null.
My requirement is - whenever the product column value (in a row) is composite (i.e. has more than one product, e.g. Bolt + Brush), the record must be split into two rows - 1 row each for the composite product types.
So, in this example, notice how the 2nd row (in the input) gets split into 2 rows -> 1 row for "Bolt" and another for the "Brush", with their price extracted from their corresponding columns (i.e in this case, "Bolt" = $3.99 and "Brush" = $6.99)
Note: For composite product values there can be at most 2 products as shown in this example (e.g. Bolt + Brush)
CustId | Product | Hammer | Bolt | Brush
--------------------------------
12345 | Hammer | $5.99 | Null | Null
53762 | **Bolt+Brush** | Null | $3.99 | $4.99
43883 | Brush | Null | Null | $4.99
I have tried creating 2 predetermined records via UNION ALL using a CTE and then main_table Left Outer Join with CTE, so that the join yields 2 records instead.
#CustId | Product | Price #
12345 | Hammer | $5.99
**53762** | **Bolt** | $3.99
**53762** | **Brush** | $4.99
43883 | Brush | $4.99
This has to be solved by Spark-SQL only.
I think this will work:
select CustId, 'Hammer' as product, Hammer
from t
where Product like '%Hammer%'
union all
select CustId, 'Bolt' as product, Bolt
from t
where Product like '%Bolt%'
union all
select CustId, 'Brush' as product, Brush
from t
where Product like '%Brush%';
This would work also
select custid, product,
case when product like '%Hammer%' then hammer
when product like '%Bolt%' then bolt
else brush end as Price from
(select custid, explode(split(product,'\\+')) as product, hammer, bolt, brush
from t) x;

Oracle WITH clause and grouping by results

I have two tables: a product table and a territory table. The product tables holds IDs of products and the territory code denoting which countries they can be sold in:
PRODUCT:
PRODUCT_ID | TERRITORY_CODE
----------------------------
PROD1 | 2
PROD2 | 0
PROD3 | 1
PROD4 | 0
PROD5 | 2
PROD6 | 0
PROD7 | 2
The second table table holds a territory code and the corresponding ISO code of countries it's allowed to be sold in. For example:
TERRITORY:
TERRITORY_CODE | COUNTRY_CODE
---------------------------
0 | US
1 | CA
2 | US
2 | CA
I would like to write a query that counts the number of PRODUCT_IDs using COUNTRY_CODE as a key.
For example, I want to know how many distinct products there are for sale in the US. I don't want to have to know that 0 and 2 are territory codes that contain the US, I just want to look up by COUNTRY_CODE. How can I do this?
In some preliminary research, I've found that a WITH clause may be useful, and came up with the following query:
WITH country AS (
SELECT (DISTINCT COUNTRY_CODE)
FROM TERRITORY
)
SELECT COUNT(DISTINCT PRODUCT_ID)
FROM country c,
PRODUCT p
WHERE p.TERRITORY_CODE=c.TERRITORY_ID;
However, this doesn't produce the expected result. I also can't get it to group by COUNTRY_CODE. What am I doing wrong?
Looks like you need to use GROUP BY. Try something like this:
SELECT T.Country_Code, COUNT(DISTINCT PRODUCT_ID)
FROM Product P
JOIN Territory T ON P.Territory_Code = T.Territory_Code
GROUP BY T.Country_Code
And the SQL Fiddle.
Good luck.

SQL Database Model: FOOD and INGREDIENTS tables

I have 2 tables: FOOD and INGREDIENTS. I want to select all ingredients that are necessary to make certain food.
I thought I could do something like this:
select NAME from INGREDIENTS
where ID in (select ING1_ID, ING2_ID, ING3_ID from FOOD where NAME = 'Meat soup')
That obviously doesn't work as I need to pass a string of comma separated IDs in the "in" sub-select query. I was looking into converting list of column values into a string and there are some solutions out there but I started to think that maybe I'm over-complicating this and my database model is wrong.
FOOD Table:
ID | NAME | ING1_ID | ING2_ID | ING3_ID
--------+---------------+----------+----------+--------
1 | Meat soup | 1 | 2 | 3
2 | Pasta Bolo | 3 | 4 | 5
3 | Chicken salad | 2 | 4 | 5
INGREDIENTS Table:
ID | NAME
--------+-----------
1 | pasta
2 | onion
3 | oil
4 | paprika
5 | chicken
6 | cucumber
Many dishes have more than three ingredients, and when you're searching, you don't care about whether a given ingredient is #1 or #2 or #3.
So a better model is
FOOD
id | name
1 | meat soup
INGREDIENTS
id | name
1 | meat
FOOD_INGREDIENTS
food_id | ingredients_id
1 | 1
Now your query is just
select i.name from ingredients i, food f, food_ingredients fi
where fi.food_id = f.id
and fi.ingredients_id = i.id
and f.name = "meat soup"
The "food_ingredients" table is often called a "join table". Another nice thing about a join table is that you can add information that's specific to the particular inclusion of an ingredient in a kind of food (such as amount, or how it should be prepared).
Your problems stem from the fact that you do not have a normalized data model.
You should redesign into something like this:
FOOD table:
FoodID
Name
INGREDIENTS table:
IngID
Name
FOOD2INGREDIENTS table:
FoodID
IngID
(adding quantity here would make a lot of sense)
Once such a redesign had been made you could get what you want simply by querying:
SELECT i.ID, I.Name
FROM Ingredients I
INNER JOIN Food2Ingredients F2I
ON I.IngID = F2I.IngID
WHERE
F2I.FoodID = :foodid;
As long as all your recipes are so impoverished as to require exactly three ingredients, then you can resolve the problem with the original query by writing:
SELECT Name FROM Ingredients
WHERE ID IN (SELECT ING1_ID FROM Food WHERE Name = 'Meat soup'
UNION
SELECT ING2_ID FROM Food WHERE Name = 'Meat soup'
UNION
SELECT ING3_ID FROM Food WHERE Name = 'Meat soup'
)
However, I think JacobM and Kerbocat are onto the right idea with the database redesign, and your sense that there was something wrong with your database design is to be commended. Note that if you need a recipe with 4 (or more) ingredients, you face a disaster with the current design (for example, because you have to edit all the existing SQL which includes multi-way UNION operations like the one shown above), but the redesigned schemas wouldn't even notice.

MySQL query for initial filling of order column

Sorry for vague question title.
I've got a table containing huge list of, say, products, belonging to different categories. There's a foreign key column indicating which category that particular product belongs to. I.e. in "bananas" row category might be 3 which indicates "fruits".
Now I added additional column "order" which is for display order within that particular category. I need to do initial ordering. Since the list is big, I dont wanna change every row by hand. Is it possible to do with one or two queries? I dont care what initial order is as long as it starts with 1 and goes up.
I cant do something like SET order = id because id counts from 1 up regardless of product category and order must start anew from 1 up for every different category.
Example of what I need to achieve:
ID | product | category | Order
1 | bananas | fruits | 1
2 | chair | furniture | 1
3 | apples | fruits | 2
4 | cola | drinks | 1
5 | mango | fruits | 3
6 | pepsi | drinks | 2
(category is actually a number because it's foreign key, in example I put names just for clarification)
As you see, order numbers start anew from 1 for each different category.
Sounds like something a SQL procedure would be handy for.
Why not just set the order to the category? That is, why not:
update Table
set SortOrder = Category;
As an aside, you cannot have a column named order -- that is a reserved word in SQL.