I've 3 tables named products, attributes and a relation table product_attributes. You can see them with example entries below.
products
id| product_name
1 | Product 1
2 | Product 2
attributes
id| attribute
1 | length
2 | width
3 | height
4 | weight
5 | color
product_attributes
id| product_id| attribute_id| value
1 | 1 | 1 | 120
2 | 1 | 2 | 60
3 | 2 | 1 | 100
4 | 2 | 3 | 40
5 | 2 | 5 | red
To show Product Details:
Product 1
Length: 120
Width: 60
Unlinked attributes for Product 1:
Height, weight, color
Product 2
Length: 100
Height: 40
Color: Red
Unlinked attributes for Product 2:
Width, Weight
The thing I want to do is mainly, when i select a product, get unlinked attributes.
For example, when I select Product 1, sql will return
Height, weight, color
I know it can be done with PHP but also know it can be done with SQL, too.
I just wanted to brainstorm a bit.
How it can be done with SQL? Especially in MySQL?
To fetch non-existing attributes for a specified product, i think you should use this query,
note that, without inner select query this query will fetch attributes whichs are not used for any of products.
SELECT attribute FROM attributes a
LEFT JOIN (SELECT * FROM product_attributes pa WHERE pa.product_id = 1 ) pa ON a.id = pa.id
WHERE pa.id IS NULL
I don't think it is the best way, but it should work.
SELECT attribute FROM attributes
WHERE id NOT IN (
SELECT attribute_id FROM product_attributes
WHERE product_id = '1')
Related
I'm trying to develop a database for my inventory. But I have no idea how to keep track of multilevel packaging.
For example:
I currently have a products and positions table
products
Id | Name
================
1013 | Metal
1014 | Wood
positions
id | Name
================
1 | 1-1-1-1
2 | 1-1-1-2
And my inventory table I was thinking of doing something like this:
Let's say I stored 1 box with 1000 Metal and 1 box with 500 Wood at position 1-1-1-1
ItemId | ProductId | Quantity | PositionId
==========================================
1 | 1013 | 1000 | 1
2 | 1014 | 500 | 1
So I'll label those two boxes with a barcode 1 and 2 respectively, so if I scan them, I can check this table to see the product and quantity inside them.
But I can also put these 2 boxes (1 and 2) inside another box (let's call it box 3), which would generate a new barcode for it that, if scanned, will show both previous boxes and its items. And store this box 3 in another position
And I can also put this box 3 inside a pallet, generating a new code and so on. So basically I can multilevel package N times.
What is the best table structure to keep track of all of this? Thanks in advance for any help!
I would add another column to the products table, make it a BIT and maybe call it BOM, BillOfMaterials, or whatever makes sense to you
So your products Table would look like this
Then you could create another table called BillOfMaterials
Quantity is how many of your products are needed to make up your new product. So for this example 2 metal and 1 wood make a pencil.
I was able to make a good structure:
My products and positions are the same but I created a stock table like:
id | product_id | amount | parent_id | position_id
=====================================================
1 | 1013 | 1000 | 4 | 1
2 | 1013 | 1000 | 4 | 1
3 | 1014 | 500 | 4 | 1
4 | 1234 | NULL | NULL | 1
The 1234 (random id) is a box that contains 2000 metal and 500 wood. I dont save this box in the product table.
When I scan the box with id 3, I perform a recursive cte query:
with recursive bom as (
select *, 1 as level
from testing.stock
where id = '4' #scanned id
union all
select c.*, p.level + 1
from testing.stock c
join bom p on c.parent_id = p.id
)
select product_id as product, sum(amount), position_id
from bom b
left join testing.product pd on b.product_id = pd.id
where pd.id is not null
group by product_id, position_id
which returns:
sum | product | position
2000 | 1013 | 3
500 | 1014 | 3
to get by position I just run a variation of the above query. To perform an update I get the Ids inside that box and run a
update testing.stock set position = '2' where id in (#variation of above query)
I hope this helps someone. This works for N packaging level
I want to be able to filter out groups where the values aren't the same. When doing the query:
SELECT
category.id as category_id,
object.id as object_id,
object.value as value
FROM
category,
object
WHERE
category.id = object.category
We get the following results:
category_id | object_id | value
-------------+-----------+-------
1 | 1 | 1
1 | 2 | 2
1 | 3 | 2
2 | 4 | 3
2 | 5 | 2
3 | 6 | 1
3 | 7 | 1
The goal: Update the query so that it yields:
category_id
-------------
1
2
In other words, find the categories where the values are different from the others in that same category.
I have tried many different methods of joining, grouping and so on, to no avail.
I know it can be done with multiple queries and then filter with a little bit of logic, but this is not the goal.
You can use aggregation:
SELECT o.category as category_id
FROM object o
GROUP BY o.category
HAVING MIN(o.value) <> MAX(o.value);
You have left the FROM clause out of your query. But as written, you don't need a JOIN at all. The object table is sufficient -- because you are only fetching the category id.
I am a beginner and I am working on a quite big query which returns about 50k of rows.
I've spent a big number of hours trying to figure out the issue and it looks that there is a gap in my knowledge and I would be really greatful if you could help me.
In order to show you the main idea I decided to simplify and split the data. I am presenting the relevant tables here:
*company*
+----+----------+---------------+----------------+
| ID | name | classificaton | special_number |
+----+----------+---------------+----------------+
| 1 | companyX | 309 | 242 |
+----+----------+---------------+----------------+
*branch*
+----+---------------+-------+
| ID | name | color |
+----+---------------+-------+
| 1 | environmental | green |
| 2 | navy | blue |
+----+---------------+-------+
*company_branch*
+------------+-----------+
| ID_company | ID_branch |
+------------+-----------+
| 1 | 1 |
| 1 | 2 |
+------------+-----------+
Ok as we have all the needed data presented I need to create a query which will select all the companies along with the main color of the branches they are working in.
A companyX can belong to more than one branch but I need to show only the main branch which can be calculated based on the three conditions below:
*if classification = 309 and special_number is even then show the relevant color and go the next company (ignore the next conditions)
*if classification = 209 and special_number is even then show the relevant color and go the next company (ignore the next condition)
*else show as grey
I created a query like that: (I know that the case phrase is not correct but I am keeping it as it shows in a better way what I am trying to accomplish)
SELECT c.ID, c.name, b.color, c.classification, c.special_number,
CASE
WHEN c.classification = 309 AND c.special_number % 2 = 0 THEN b.color
WHEN c.classification = 209 AND c.special_number % 2 = 0 THEN b.color
ELSE 'grey'
END AS 'case'
FROM company c INNER JOIN company_branch cb ON c.ID = cb.ID_company
INNER JOIN branch b ON b.ID = cb.ID_branch
then I get the following result
*result*
+----+----------+-------+----------------+----------------+------+
| ID | name | color | classification | special_number | case |
+----+----------+-------+----------------+----------------+------+
| 1 | companyX | green | 309 | 242 | green|
| 1 | companyX | blue | 309 | 242 | blue |
+----+----------+-------+----------------+----------------+------+
The problem is that if any company belongs to more than one branch then I always get many colors... what I would like to get is a list of companies with only one color of the main branch they are working in.
can you guys help the newbie ?
I think you want something like this:
SELECT DISTINCT ON (c.id) c.ID, c.name,
COALESCE(b.color, 'grey') as color
c.classification, c.special_number,
FROM company c LEFT JOIN
company_branch cb
ON c.ID = cb.ID_company LEFT JOIN
branch b
ON b.ID = cb.ID_branch
ORDER BY c.Id,
(CASE WHEN c.classification = 309 AND c.special_number % 2 = 0 THEN 1
WHEN c.classification = 209 AND c.special_number % 2 = 0 THEN 2
ELSE 3
END);
DISTINCT ON is a (useful) Postgres extension that returns one row per item(s) in parentheses. The returned row is based on the GROUP BY. The first key(s) in the ORDER BY need to be the item(s). The following specifies what "first" means.
I switched the joins to being outer joins, so all companies are in the result set.
For a project I want to generate a price list.
I want to get only the latest prices from each supplier for each article.
There are just those two tables.
Table articles
ARTNR | TXT | ACTIVE | SUPPLIER
------------------------------------------
10 | APPLE | Y | 10
20 | ORANGE | Y | 10
30 | KEYBOARD | N | 20
40 | ORANGE | Y | 20
50 | BANANA | Y | 10
60 | CHERRY | Y | 10
Table prices
ARTNR | PRCGRP | PRCDAT | PRICE
--------------------------------------
10 | 10 | 01-Aug-10 | 2.1
10 | 10 | 05-Aug-11 | 2.2
10 | 10 | 21-Aug-12 | 2.5
20 | 0 | 01-Aug-10 | 2.1
20 | 10 | 09-Aug-12 | 2.3
10 | 10 | 14-Aug-13 | 2.7
This is what I have so far:
SELECT
ARTICLES.[ARTNR], ARTICLES.[TXT], ARTICLES.[ACTIVE], ARTICLES.[SUPPLIER], PRICES.PRCGRP, PRICES.PRCDAT, PRICES.PRICE
FROM
ARTICLES INNER JOIN PRICES ON ARTICLES.ARTNR = PRICES.ARTNR
WHERE
(
(ARTICLES.[ACTIVE]="Y") AND
(ARTICLES.[SUPPLIER]=10) AND
(PRICES.PRCGRP=0) AND
(PRICES.PRCDAT=(SELECT MAX(PRCDAT) FROM PRICES as art WHERE art.ARTNR = PRICES.artnr) )
)
ORDER BY ARTICLES.ARTNR
;
It is okay to choose just one supplier each time, but I want the max price.
The problem is:
Lots of articles do not show up with the query above,
but I cannot figure out what is wrong.
I can see that they should be in the resultset when I leave out the subselect on max prcdat.
What is wrong?
Your subquery to get the latest price does not take the other conditions into account, that is when you're getting the latest price, you may get a price in another price group or that is not active. When you join that against the filtered list that has no inactive prices and only prices in a single price group, you get no hits that exist in both.
Either you need to duplicate or - better - move your conditions inside the subquery to get the best price under the conditions. I can't test against access, but something like this should be possible if the SQL is not too limited;
SELECT a.artnr, a.txt, a.active, a.supplier, p.prcgrp, p.prcdat, p.price
FROM articles a INNER JOIN prices p ON a.ARTNR = p.ARTNR
JOIN (
SELECT a.artnr, MAX(p.prcdat) prcdat
FROM articles a JOIN prices p ON a.artnr = p.artnr
WHERE a.active='Y' AND a.supplier=10 AND p.prcgrp=10
GROUP BY a.artnr) z
ON a.artnr = z.artnr AND p.prcdat = z.prcdat
ORDER BY a.ARTNR
If the SQL support in access won't allow a join with a subquery, you can just move the conditions inside your existing subquery, something like;
SELECT a.artnr, a.txt, a.active, a.supplier, p.prcgrp, p.prcdat, p.price
FROM articles a INNER JOIN prices p ON a.ARTNR = p.ARTNR
WHERE p.prcdat = (
SELECT MAX(p2.prcdat)
FROM articles a2 JOIN prices p2 ON a2.artnr = p2.artnr
WHERE a.artnr = a2.artnr AND a2.active='Y' AND a2.supplier=10 AND p2.prcgrp=10
)
ORDER BY a.ARTNR;
Note that due to limitations in identifying a unique price (no primary key in prices), the queries may give duplicates if several prices for the same article have the same prcdat. If that's a problem, you'll probably need to duplicate your conditions outside the subquery too.
I have two tables: Issue and Return. Both tables have columns article_id, person_id and quantity. I need to subtract Return table from Issue table for one person and group the result by article name.
First select looks like this:
SELECT Article.Name, SUM(Issue.Quantity)
FROM Issue LEFT JOIN Article ON (Issue.ArticleID = Article.ID)
WHERE Issue.PersonID = 2
GROUP BY Article.Name
ArticleName | Quantity
------------------------
Shoes | 5
Coats | 3
Hats | 3
Second like this:
SELECT Article.Name, SUM(Return.Quantity)
FROM Return LEFT JOIN Article ON (Return.ArticleID = Article.ID)
WHERE Return.PersonID = 2
GROUP BY Article.Name
ArticleName | Quantity
------------------------
Shoes | 3
Coats | 2
Hats | 0
Question is, how do I subtract second select from the first (Return - Issue) to get a table like this:
ArticleName | Quantity
------------------------
Shoes | 2
Coats | 1
Hats | 3
The Quantity in the final result should be the number of remaining articles to be returned.
This may work:
(heavily edited - my first answer did not have a chance of working...)
SELECT Article.Name
, (SELECT SUM(Issue.Quantity) FROM Issue WHERE Issue.ArticleID = Article.ID AND Issue.PersonID = 2)
- (SELECT SUM(Return.Quantity) FROM Return WHERE Return.ArticleID = Article.ID AND Return.PersonID = 2)
FROM Article