Getting a sum from nested many to many relations based on another relation - sql

I have the following table structure:
calculations
- id
persons
- id
calculations_persons
- id
- calculation_id
- person_id
assets
- id
- person_id
- type (enum: normal/stock)
- stock_id (nullable)
- value
stocks
- id
- value
the relations are like so:
calculations <- Many to Many -> persons
persons | Has Many -> assets
stocks | can Belong To -> assets
Basically, I want to get the calculations table ordered by the value of all assets, but there is a catch, the calculation of assets is based on the fact that if a asset is of type stock then it's value is based on the value of the asset * the stocks value
so if I have for example a calculation with id: 1 with personA with id 1 with assetA of value 50 and assetB of value 100 (type stock) with it's stock value being 20 and person with id 2 with assetA of value 100 the result would look like this
calculation.id, (personA.id, personB.id, ...etc persons), total value
1, (1 , 2), 2150
How would the SQL look like? I'm using postgres

Related

SQL Server - Update field in all table records, using a field from the same table and values from other table

I have this scenario:
Table Territory
ID (int) - CODE (varchar) - NAME (varchar)
Data:
1 - GB - UNITED KINGDOM
2 - GB - ISLE OF MAN
3 - GB - NORTHERN IRELAND
4 - PT - PORTUGAL
5 - DE - GERMANY
6 - DE - HELGOLAND ISLAND
Table Rules:
ID (int) - TERRITORY_CODES (varchar) - TERRITORY_IDS (varchar)
1 - 'GB,PT' - NULL
2 - 'DE,PT' - NULL
I know the second table should not be like this, but I have no option to change it.
I want to fill the column TERRITORY_IDS with the IDs from the table TERRITORY separated by comma. For example:
Table Rules
ID (int) - TERRITORY_CODES (varchar) - TERRITORY_IDS (varchar)
1 - 'GB,PT' - '1,4'
2 - 'DE,PT' - '5,4'
There are several IDs for each territory code, but I want only one ID for each territory table, it could be the first one, doesn't matter.
What you are looking to do is a Bad Idea. It is a good thing that you recognize this is a bad Idea. But for those reading this question and do not understand why it is bad, this violates the First normal form (1NF) principle. Which is all columns should be atomic, meaning that they hold 1 and only 1 value.
Lets get to the nuts and bolts on how to do this Coalesce to the rescue.
Since I do not know why 'gb,pt' and 'de,pt' are grouped that way I didnt wrap this in a Cursor to go through the whole table. But you can easily wrap this in a cursor and do the entire table contents for you.
DECLARE #TERRITORY_Ids varchar(100)
SELECT #TERRITORY_Ids = COALESCE(#TERRITORY_Ids+ ', ', '') +
Id
FROM table_terrytory
WHERE code in ('gb','pt')
INSERT INTO table_rules
SELECT 'gb,pt',#TERRITORY_Ids

One to many relationship on the same table

Here is the situation:-
I have a table called Users. This contains user data for students and tutors as most of the data required is the same.
Having completed the system I am now told that the client would like to be able to assign students to tutors.
Is there a legitimate/ clean way I can create a one to many relationship within a single table, perhaps via a link table?
I've tried to think this through but whatever solution I come up with seems messy.
I would be grateful for any input.
Thanks
Phill
Have you tried the following approach?
Make a new table, for example TutorStudent (choose a more appropriate name if needed). It should have two columns:
Tutor_ID
Student_ID
Both columns shall be the (composite) primary key, each column will be a foreign key to your Users table User_ID (I assume this is what you have).
So, if you have a tutor named Newton that has two students, Tesla and Edison, your Users table will have something like this:
User_ID, Name
1, Newton
2, Tesla
3, Edison
and your TutorStudent table will have following values:
Tutor_ID, Student_ID
1, 2
1, 3
Relatively simple and doesn't require any modifications to your existing table.
Do take care when deleting users - use the delete cascade feature of your database system or do some maintenance work afterwards so your TutorStudent table doesn't go stale when updating/removing your users.
My ideal for the same situation
Example: one book have many category:
Basic solution:
book table has recorded book information
category table has recored category information ex: 100 documents
book_category_relation table has single book (book_id) has category(category_id) 1 book may be have 100 category_id
Ideal solution:
First calculate total your category: ex 100 document. Each category equal value 1 bit: max 31 bit so 100 category we have ceil floor(100%31) = 4 groups
category_id = 1 : 1 (1%31) <=> 000000001 group 0 = floor(1/31)
category_id = 2 : 2 (2%31)<=> 000000010 group 0 = floor(2/31)
category_id = 3 : 4 (3%31)<=> 000000100 group 0 = floor(3/31)
category_id = 4 : 8(4%31)<=> 000001000 group 0 = floor(4/31)
...........................
category_id = 31: 2^31(31%31) <=>1000..000 group 0 if moduler 31 equal zero so number group = (31/31 -1)=0;
category_id = 32: 1(32%31) <=> 0000000001 group 1 = floor(32/31)
category_id = 33: 2(33%31) <=> 0000000010 group 1 = floor(33/31)
Ok now we add 4 fields in design book table (group_0,group_1,group_2,group_3) with int(11) unsigned and add index that fields
if book has category id = n so we can the following calculate formula:
bit code = (n%31 ==0)?31: (n%31)
number group field = (n%31==0)?(n/31 -1):floor(n/31)
ex: book in category_id = 100 so:
bit code = (100%31) =7 <=>2^7 = 128,
group = floor(100%31) = 3 <=> in group_3
so if you need query all book in category_id = 100, query string is:
SELECT * FROM book WHERE group_3&128
Note: MySQL not index working if bitwise in where.
But you can check in this link:
Bitwise operations and indexes

Two types of invoice numbering (2 tables with 0..1 relationship)

I have table invoices - PK is year+document_type_id+number (this is current numbering, and i can't change it). So, the data is like this:
year document_type_id number
2013 351 1
2013 351 2
2013 352 1
Now, i need to develop second type of numbering - table invoices_2 - PK is year+market_id+cash_register_id+number (this is numbering for some of invoices which is proscribed by the law), and FK is invoices_year+invoices_document_type_id+invoices_number.
invoice - invoice_2 must be 1 -> 0..1 relationship.
The problem is that in invoices_2 table i may have this (which i would like to eliminate - using some PK+FK combination?):
year market_id cash_register_id number invoices_year inovices_document_type invoices_number
2013 1 1 1 2013 351 1
2013 1 1 2 2013 351 1
As you can see, the invoice 2013-351-1 using can be added more that 1 times in invoices_2 table, which must be forbidden.
See http://www.sqlfiddle.com/#!3/6b42c/1
It would be better to put the second numbering system in its own table.
Invoice
-------
year
document_type_id
number
invoice_2_fk
...
Invoice_2
---------
invoice_2 _id
year
market_id
cash_register_id
number
The invoice 2 foreign key in the Invoice table is nullable. When it's null, there is no invoice 2. When it's a valid id, then there is an invoice 2. This is a one to zero / one relationship.

Fill a drop-down list parameter with specific available values

I have an SSRS report that creates report from a SQL table:
id type name
1 fruit wilk
2 fruit scot
3 paper jon
4 pen brad
5 tape lin
6 water james
The report has two data sets: one feeds query for report, and the other feeds data to parameter. So in the report the multi-value parameter gets its values from dataset2.
-- dataset1::
select ID, TYPE, name from table1 where type in (#types)
-- dataset2::
select TYPE from table1
The report is generated based on type selected from dropdown list (which is a multi select list).
For example if we select "fruit" the report displays:
wilk, scot
If we select "water":
james
Now the thing is that I need to create a name for all the values "TAPE", "pen", and "paper", say the name "STATIONARY", such that the dropdown list should show only:
fruit, stationary, water
And when I select "STATIONARY" from thedropdown list the report should display:
jon, brad, lin (all 3 have some form of stationary, i.e paper, pen, tape)
And when I select type as "STATIONARY" and "water" it should display:
jon, brad, lin, james
Just from the hip here.
Consider adding a category field to your table. So for your fruit and water you could have a category called "Food", and for your pen, paper, and tape the category would be called "stationary".
Add another dataset to your report called "category".
SELECT Category FROM table1
Add another parameter that is a multiple selection of your new data set called #Category.
In your main query add:
...AND Category IN (#Category)
EDIT
Keep in mind this advice completely ignores normalization in your database. I understand that is not the intent of your question but it is something you should always consider. If it were me I would even add a category table. Then with the "table1" as you call it I would add a foriegn key pointing at an ID in the category table. You can even see this issue with your type column. Notice how fruit is used more than once.
I'd create another couple of tables for this called Item and ItemType.
ItemType has two fields: ItemTypeId (the auto-incrementing primary key) and Name. ItemType will have values like:
ItemTypeId Name
1 Food
2 Stationery
Item has three fields: ItemId (the auto-incrementing primary key), Name and ItemTypeId (from the ItemType table above). It looks like this:
ItemId Name ItemTypeId
1 Fruit 1
2 Paper 2
3 Pen 2
4 Tape 2
5 Water 1
Add the ItemId field to table1 and remove the type field, so it now looks like:
id ItemId name
1 1 wilk
2 1 scot
3 2 jon
4 3 brad
5 4 lin
6 5 james
We now know the type of the item from the link to the ItemType.
Create two parameters: #ItemTypes and #Items as multi-value.
#ItemTypes populates from the ItemType table:
SELECT ItemTypeId, Name FROM ItemType
ItemTypeId is the Value and Name is the Label.
#Items populates from the Item table but is filtered on the #ItemTypes parameter like so:
SELECT ItemId, Name FROM Item WHERE (ItemTypeId IN #ItemTypes)
ItemId is the Value and Name is the Label.
Now when you select #ItemTypes in the first parameter, the second parameter will only show items of that type.
Okay, back to your query. Your main query now looks like:
SELECT Item.Name AS ItemName, ItemType.Name AS ItemTypeName, table1.Name
FROM table1
INNER JOIN Item ON Item.ItemId = table1.ItemId
INNER JOIN ItemType ON ItemType.ItemTypeId = Item.ItemTypeI
WHERE (ItemType.ItemTypeId IN #ItemTypes)
AND (Item.ItemId IN #Items)
and I think our work here is done.

Help Me With This MS-Access Query

I have 2 tables: "products" and "pieces"
PRODUCTS
idProd
product
price
PIECES
id
idProdMain
idProdChild
quant
idProdMain and idProdChild are related with the table: "products".
Other considerations is that 1 product can have some pieces and 1 product can be a piece.
Price product equal a sum of quantity * price of all their pieces.
"Products" table contains all products (p
EXAMPLE:
TABLE PRODUCTS (idProd - product - price)
1 - Computer - 300€
2 - Hard Disk - 100€
3 - Memory - 50€
4 - Main Board - 100€
5 - Software - 50€
6 - CDroms 100 un. - 30€
TABLE PIECES (id - idProdMain - idProdChild - Quant.)
1 - 1 - 2 - 1
2 - 1 - 3 - 2
3 - 1 - 4 - 1
WHAT I NEED?
I need update the price of the main product when the price of the product child (piece) is changed.
Following the previous example, if I change the price of this product "memory" (is a piece too) to 60€, then product "Computer" will must change his price to 320€
How I can do it using queries?
Already I have tried this to obtain the price of the main product, but not runs. This query not returns any value:
SELECT Sum(products.price*pieces.quant) AS Expr1
FROM products LEFT JOIN pieces ON (products.idProd = pieces.idProdChild) AND (products.idProd = pieces.idProdChild) AND (products.idProd = pieces.idProdMain)
WHERE (((pieces.idProdMain)=5));
MORE INFO
The table "products" contains all the products to sell that it is in the shop.
The table "pieces" is to take a control of the compound products. To know those who are the products children. For example of compound product: computers. This product is composed by other products (motherboard, hard disk, memory, cpu, etc.)
You have a duplicate join:
(products.idProd = pieces.idProdChild) AND (products.idProd = pieces.idProdChild)
and the other join should most likely be an OR:
LEFT JOIN pieces ON (products.idProd = pieces.idProdChild) OR (products.idProd = pieces.idProdMain)
from what I can tell by the table structure. Not sure what "vinculated" means exactly, I'm guessing "releated". If that is the case, I'm not sure how you're relating both idProductMain and idProductChild to the Product Table. Maybe some samples of the data would help.
EDIT:
Ok, looks like the join is backwards
LEFT JOIN pieces ON (products.idProdChild = pieces.idProd) OR (products.idProdMain = pieces.idProd)
I think that will get you most of the way there.
I think what you are looking for is much simpler than what you tried so far.
Essentially all you need to do is to find all the products-and-quantities (see INNER JOIN clause below) that are components of the given product (see WHERE clause) and sum price*quantities up (see SELECT clause):
SELECT
SUM(products.price * pieces.quant) as totalPrice
FROM
pieces INNER JOIN products ON (products.idProd = pieces.idProdChild)
WHERE
pieces.idProdMain = x
When you run it, it asks you for a value for 'x', which is the ID of the product whose total price you need to calculate.
I hope this helps.