Return multiple columns from subquery as Columns - sql

Here is my data.
Products: <Where all list of SKU are stored>
Prod_ID | Prod_Desc | Base_Unit_ID
1 | Custom Product | 1
UOM: <Masterlist for Unit of measures>
UOM_ID | Desc
1 | Piece
2 | Box
3 | Case
UOM_Conversion: <From base unit. Multiplier is how many base unit in a To_Unit>
Prod_ID | from_Unit_ID | Multiplier | To_Unit_ID
1 | 1 | 100 | 2
1 | 1 | 400 | 3
Given This Data. How Can I display it like this?
Product | Base Unit | Pack Unit | Multiplier | Case Unit | Multiplier
Custom Product | Piece | Box | 100 | Case | 400
I have tried Left Join Lateral but sadly. What it does is return two rows.
The reason I want to do this is because Im developing a module which stores products with multiple Unit of measure(Max of 3). So I dont want to create 3 columns of Unit, Pack and Case hence the reason I created the UOM_conversion table.

I think there is a global misconception here but I will give you it after the solution for your problem. I advise you to look at it.
So firstly : what you need here is the crosstab() function.
CREATE EXTENSION tablefunc;
For the following script :
create table uom
(
uom_id int primary key
, description varchar (250)
);
create table products
(
prod_id int primary key
, prod_desc varchar(250)
, base_unit_id int references uom (uom_id)
);
create table uom_conversion
(
prod_id int references products (prod_id)
, from_unit_id int references uom (uom_id)
, multiplier int
, to_unit_id int references uom (uom_id)
);
insert into uom values (1, 'Piece'), (2, 'Box'), (3, 'Case');
insert into products values (1, 'Custom Product', 1);
insert into uom_conversion values (1,1,100,2), (1,1,400,3);
The request is :
select
p.prod_desc as "Product"
, u.description as "Base Unit"
, u2.description as "Pack Unit"
, final_res."1" as "Multiplier"
, u3.description as "Case Unit"
, final_res."2" as "Multiplier"
from crosstab(
'select
p.prod_id
, base_unit_id
, multiplier
from products p
inner join uom_conversion uc
on uc.prod_id = p.prod_id')
as final_res (prod_id int, "1" int, "2" int)
inner join crosstab('select
uc.prod_id
, u.description
, uc.to_unit_id
from uom_conversion uc
inner join uom u
on u.uom_id = uc.to_unit_id')
as final_res_2 (prod_id int, "Box" int, "Case" int)
on final_res.prod_id = final_res_2.prod_id
inner join products p
on p.prod_id = final_res.prod_id
inner join uom u
on p.base_unit_id = u.uom_id
inner join uom u2
on u2.uom_id = final_res_2."Box"
inner join uom u3
on u3.uom_id = final_res_2."Case";
This is solving your problem. BUT : How do you know the order of what is pack_unit and what is the case_unit? I think from this question a lot more will come up.

Related

How to use Count SQL Server Query

i write this post to ask for help on a query on the following schema:
Imagine having two formats, for example, PDF and EBOOK. My goal is to get the following result:
PushiblerName | Number_PDF | Numerber_BOOK |
--------------------------------------------------
ExampleName1 | 2 | 0
ExampleName2 | 3 | 1
Would anyone help me write this query?
Thanks in advance.
select name,(select count(*) from t_book where IdPublisher = t_publisher.Id) as Numerber_BOOK,
(select count(*) from t_book where IdPublisher = t_publisher.Id and IdFormat = 3) as Number_PDF from t_publisher
replace 3 with the Id of Pdf format
Try this:
select P.Name as PublisherName,
SUM(case when F.Code = 'PDF' then 1 else 0 end) as Number_PDF,
SUM(case when F.Code = 'BOOK' then 1 else 0 end) as Number_BOOK
from T_BOOK as B
join T_BOOK_FORMAT as BF on B.Id = BF.BookId
join T_FORMAT as F on BF.IdFormat = F.Id
join T_PUBLISHER as P on B.IdPublisher = P.Id
group by P.Name
I assumed, that format of a book is held in T_FORMAT table in column Code (part with F.Code).
You can use this code,
SELECT publisher.Name as PublisherName,
(SELECT COUNT(*)
FROM T_BOOK book
JOIN T_BOOK_FORMAT bookformat ON book.Id = bookformat.IdBook
JOIN T_FORMAT format ON bookformat.IdFormat = format.Id
WHERE book.IdPublisher = publisher.Id and format.Code='PDF'
) AS Number_PDF,
(SELECT COUNT(*)
FROM T_BOOK book
JOIN T_BOOK_FORMAT bookformat ON book.Id = bookformat.IdBook
JOIN T_FORMAT format ON bookformat.IdFormat = format.Id
WHERE book.IdPublisher = publisher.Id and format.Code='EBOOK'
) AS Number_BOOK
FROM T_PUBLISHER publisher
GROUP BY publisher.Id
I guess you can try something like this
first setup some sample data:
declare #t_book table (Id int, IdPublisher int)
declare #t_publisher table (Id int, Name varchar(50))
declare #t_book_format table (IdBook int, idFormat int)
declare #t_format table (Id int, Description varchar(50))
insert into #t_book values (1, 1), (2, 1), (3, 2), (4, 2), (5, 2), (6, 2)
insert into #t_publisher values (1, 'examplename1'), (2, 'examplename2')
insert into #t_book_format values (1, 1), (2, 1), (3, 2), (4, 1), (5, 1), (6, 1)
insert into #t_format values (1 ,'pdf'), (2, 'book')
now the query :
select p.Name as PublisherName,
f.Description,
count(f.Id) as numbers
from #t_book b
inner join #t_publisher p on b.idPublisher = p.Id
inner join #t_book_format bf on b.Id = bf.idBook
inner join #t_format f on bf.idFormat = f.Id
group by p.Name, f.Description
But without sample data from all tables this is just guessing
The result is in a different format, but it still works if you add more formats in t_book_format and t_format
PublisherName Description numbers
------------- ----------- -------
examplename1 pdf 2
examplename2 book 1
examplename2 pdf 3
If you want to have the list as in you question, you could do a pivot.
But when there is a new format, this new format will not be in the result, so you have to modify the query everytime a new format is added
select * from ( select p.Name,
f.Description,
count(f.Id) as numbers
from #t_book b
inner join #t_publisher p on b.idPublisher = p.Id
inner join #t_book_format bf on b.Id = bf.idBook
inner join #t_format f on bf.idFormat = f.Id
group by p.Name, f.Description
) t
pivot
( sum(numbers)
for Description in (pdf, book)
) piv
the result now looks like this
Name pdf book
---- --- ----
examplename1 2 null
examplename2 3 1
I guess you could put this in a stored procedure and build the last query in dynamic sql, so it would still keep working when you add new formats
Here you go:
CREATE TABLE Author (
AuthorID INT IDENTITY (1,1) PRIMARY KEY NOT NULL,
AuthorName VARCHAR (25) NOT NULL
);
CREATE TABLE Books (
BookID INT IDENTITY (1,1) PRIMARY KEY NOT NULL,
BookName VARCHAR (25) NOT NULL,
BookAothor INT NOT NULL,
CONSTRAINT PK_AuthorOfBook FOREIGN KEY (BookAothor) REFERENCES Author (AuthorID)
);
CREATE TABLE Formats (
FormatID INT IDENTITY (1,1) PRIMARY KEY NOT NULL,
FormatName VARCHAR (10) NOT NULL
);
CREATE TABLE BookFormat (
BookID INT NOT NULL,
FormatID INT NOT NULL,
CONSTRAINT PK_BookFormats PRIMARY KEY (BookID, FormatID),
CONSTRAINT FK_BookRef FOREIGN KEY (BookID) REFERENCES Books (BookID),
CONSTRAINT FK_FormatRef FOREIGN KEY (FormatID) REFERENCES Formats (FormatID)
);
INSERT INTO Author VALUES
('Author1'), ('Author2'), ('Author3');
INSERT INTO Books VALUES
('Book1', 1), ('Book2', 1),
('Book3', 2), ('Book4', 2),
('Book5', 2), ('Book6', 2);
INSERT INTO Formats VALUES
('PDF'), ('E-Book');
INSERT INTO BookFormat VALUES
(1, 1), (2,1),
(3,1), (4,1), (5,1), (6,2);
SELECT A.AuthorName,
ISNULL(Pdf.PdfBooks, 0) AS PdfBooks,
ISNULL(EBook.EBooks, 0) AS EBooks
FROM
Author A LEFT JOIN
(
SELECT A.AuthorID, COUNT(B.BookID) AS PdfBooks
FROM Author A JOIN Books B ON A.AuthorID = B.BookAothor
INNER JOIN BookFormat BF ON B.BookID = BF.BookID
WHERE BF.FormatID =1
GROUP BY A.AuthorID
) Pdf ON A.AuthorID = Pdf.AuthorID
FULL JOIN
(
SELECT A.AuthorID, COUNT(B.BookID) AS EBooks
FROM Author A JOIN Books B ON A.AuthorID = B.BookAothor
INNER JOIN BookFormat BF ON B.BookID = BF.BookID
WHERE BF.FormatID =2
GROUP BY A.AuthorID
) EBook
ON Pdf.AuthorID = EBook.AuthorID;
Results:
+------------+----------+--------+
| AuthorName | PdfBooks | EBooks |
+------------+----------+--------+
| Author1 | 2 | 0 |
| Author2 | 3 | 1 |
| Author3 | 0 | 0 |
+------------+----------+--------+
Here is a demo

SQL query to output all results but only one specific value from a list without duplicating

I have a table of people and a table of client types (linked by a 3rd table called client type details) which these people are linked to. Client types could be 'Friend', 'Enemy', 'Alien', or 'Monster'. Some people can have more than one of these and some people have none.
What I'm really trying to get is an output something like:
ID | Name | Friend | Enemy | Alien | Monster |
35 | John | Friend | -blank- | -blank- | Monster |
42 | Eric | -blank- | -blank- | -blank- | -blank- |
So John is both a Friend and a Monster whereas Eric isn't any. With the query I have tried creating (just with a column for the Friends in the first instant) I am getting a row for everyone but for those who are Friends I am getting 2 rows for them - one to say they are a Friend and one to say NULL
Does any of this make sense?
Query below:
SELECT DISTINCT
cl.ClientID,
cl.dcFirstName,
(SELECT Dwh.DimClientTypes.dctName
WHERE (Dwh.DimClientTypes.dctGuid IN ('52CD80A6-D4D7-4FD3-8AE8-644A40FEC108'))
) AS Friend
FROM Dwh.DimClientTypeDetails
LEFT OUTER JOIN Dwh.DimClientTypes ON Dwh.DimClientTypeDetails.dctdTypeGuid = Dwh.DimClientTypes.dctGuid
LEFT OUTER JOIN Dwh.DimClients AS cl ON Dwh.DimClientTypeDetails.dctdClientGuid = cl.dcClientGUID
I'm really not sure the best way of approaching it so any help/advice would be very gratefully received.
Thanks
Lee
You are basically looking for a pivot with strings, and you could write it using the pivot or apply but this seems simpler.
select
Id = cl.ClientId
, Name = cl.dcFirstName
, Friend = max(case when ct.dctName = 'Friend' then ct.dctName else null end)
, Enemy = max(case when ct.dctName = 'Enemy' then ct.dctName else null end)
, Alien = max(case when ct.dctName = 'Alien' then ct.dctName else null end)
, Monster = max(case when ct.dctName = 'Monster' then ct.dctName else null end)
from Dwh.DimClients as cl
left join Dwh.DimClientTypeDetails ctd on ctd.dctdClientGuid = cl.dcClientguid
left join Dwh.DimClientTypes ct on ct.dctGuid = ctd.dctdTypeGuid
group by cl.ClientId, cl.dcFirstName
For a pivot version, this is a good example: http://rextester.com/XDACE35377
create table #t (Id int not null, Name varchar(32) not null, ClientType varchar(32) null)
insert into #t values
(35, 'John', 'Friend')
,(35, 'John', 'Monster')
,(42, 'Eric', null);
select
Id
, Name
, pvt.Friend
, pvt.Enemy
, pvt.Alien
, pvt.Monster
from #t
pivot (max(ClientType) for ClientType in ([Friend],[Enemy],[Alien],[Monster])) pvt
This pivot could be done on your schema with something like this:
with c as (
select
Id = cl.ClientId
, Name = cl.dcFirstName
, ClientType = ct.dctName
from Dwh.DimClients as cl
left join Dwh.DimClientTypeDetails ctd on ctd.dctdClientGuid = cl.dcClientguid
left join Dwh.DimClientTypes ct on ct.dctGuid = ctd.dctdTypeGuid
)
select
Id
, Name
, pvt.Friend
, pvt.Enemy
, pvt.Alien
, pvt.Monster
from c
pivot (max(ClientType) for ClientType in ([Friend],[Enemy],[Alien],[Monster])) pvt

group by not grouping aggregate?

Let's say I am trying to build an opinion poll app, such that I can create a template of an opinion poll, give it multiple sections/questions, assign multiple people to different copies of a given question, create varying measures (happyness, succesfulness, greenness) and assign different questions different weights to apply to all of these measures.
Something like so:
CREATE TABLE users (
id SERIAL NOT NULL PRIMARY KEY
);
CREATE TABLE opinion_poll_templates (
id SERIAL NOT NULL PRIMARY KEY
);
CREATE TABLE opinion_poll_instances (
id SERIAL NOT NULL PRIMARY KEY,
template_id INTEGER NOT NULL REFERENCES opinion_poll_templates(id)
);
CREATE TABLE section_templates (
id SERIAL NOT NULL PRIMARY KEY,
opinion_poll_id INTEGER NOT NULL REFERENCES opinion_poll_templates(id)
);
CREATE TABLE section_instances (
id SERIAL NOT NULL PRIMARY KEY,
opinion_poll_id INTEGER NOT NULL REFERENCES opinion_poll_instances(id),
template_id INTEGER NOT NULL REFERENCES section_templates(id)
);
CREATE TABLE question_templates (
id SERIAL NOT NULL PRIMARY KEY,
section_id INTEGER NOT NULL REFERENCES section_templates(id)
);
CREATE TABLE measure_templates (
id SERIAL NOT NULL PRIMARY KEY,
opinion_poll_id INTEGER NOT NULL REFERENCES opinion_poll_templates(id)
);
CREATE TABLE answer_options (
id SERIAL NOT NULL PRIMARY KEY,
question_template_id INTEGER NOT NULL REFERENCES question_templates(id),
weight FLOAT8
);
CREATE TABLE question_instances (
id SERIAL NOT NULL PRIMARY KEY,
template_id INTEGER NOT NULL REFERENCES question_templates(id),
opinion_poll_id INTEGER NOT NULL REFERENCES opinion_poll_instances(id),
section_id INTEGER NOT NULL REFERENCES section_instances(id),
answer_option_id INTEGER NOT NULL REFERENCES answer_options(id),
contributor_id INTEGER
);
CREATE TABLE measure_instances (
id SERIAL NOT NULL PRIMARY KEY,
opinion_poll_id INTEGER NOT NULL REFERENCES opinion_poll_instances(id),
template_id INTEGER NOT NULL REFERENCES measure_templates(id),
total_score INTEGER
);
CREATE TABLE scores (
id SERIAL NOT NULL PRIMARY KEY,
question_template_id INTEGER NOT NULL REFERENCES question_templates(id),
measure_template_id INTEGER NOT NULL REFERENCES measure_templates(id),
score INTEGER NOT NULL
);
Now let's say I am interested in the per measureInstance (per measure assigned to an opinion poll) cross question, cross user average?
WITH weighted_score AS (
SELECT AVG(answer_options.weight), measure_instances.id
FROM question_instances
INNER JOIN answer_options ON question_instances.template_id = answer_options.question_template_id
INNER JOIN scores ON question_instances.template_id = scores.question_template_id
INNER JOIN measure_instances ON measure_instances.template_id=scores.measure_template_id
WHERE measure_instances.opinion_poll_id = question_instances.opinion_poll_id
GROUP BY measure_instances.id
)
UPDATE measure_instances
SET total_score=(SELECT avg FROM weighted_score
WHERE weighted_score.id = measure_instances.id)*100
RETURNING total_score;
This seems to not only not group as expected, but produced incorrect results.
Why is the result an integer rather then a float? Why is the result not being grouped by measure instance instead being identical across all?
And why is the result incorrect for any of them?
A demonstration: http://sqlfiddle.com/#!15/dcce8/1
EDIT: In working through explaining exactly what I wanted, I realized the source of my problem was that I was simply adding percentages, rather then normalizing across questions as a percentage.
My new and improved sql is:
WITH per_question_percentage AS (
SELECT SUM(answer_options.weight)/COUNT(question_instances.id) percentage, question_templates.id qid, opinion_poll_instances.id oid
FROM question_instances
INNER JOIN answer_options ON question_instances.answer_option_id = answer_options.id
INNER JOIN question_templates ON question_templates.id = question_instances.template_id
INNER JOIN opinion_poll_instances ON opinion_poll_instances.id = question_instances.opinion_poll_id
GROUP BY question_templates.id, opinion_poll_instances.id
), max_per_measure AS (
SELECT SUM(scores.score), measure_instances.id mid, measure_instances.opinion_poll_id oid
FROM measure_instances
INNER JOIN scores ON scores.measure_template_id=measure_instances.template_id
GROUP BY measure_instances.id, measure_instances.opinion_poll_id
), per_measure_per_opinion_poll AS (
SELECT per_question_percentage.percentage * scores.score score, measure_instances.id mid, measure_instances.opinion_poll_id oid
FROM question_instances
INNER JOIN scores ON question_instances.template_id = scores.question_template_id
INNER JOIN measure_instances ON measure_instances.template_id = scores.measure_template_id
INNER JOIN max_per_measure ON measure_instances.id = max_per_measure.mid
INNER JOIN per_question_percentage ON per_question_percentage.qid = question_instances.template_id
WHERE measure_instances.opinion_poll_id = question_instances.opinion_poll_id AND question_instances.opinion_poll_id = per_question_percentage.oid
GROUP BY measure_instances.id, measure_instances.opinion_poll_id, per_question_percentage.percentage, scores.score
)
UPDATE measure_instances
SET total_score = subquery.result*100
FROM (SELECT SUM(per_measure_per_opinion_poll.score)/max_per_measure.sum result, per_measure_per_opinion_poll.mid, per_measure_per_opinion_poll.oid
FROM max_per_measure, per_measure_per_opinion_poll
WHERE per_measure_per_opinion_poll.mid = max_per_measure.mid
AND per_measure_per_opinion_poll.oid = max_per_measure.oid
GROUP BY max_per_measure.sum, per_measure_per_opinion_poll.mid, per_measure_per_opinion_poll.oid)
AS subquery(result, mid, oid)
WHERE measure_instances.id = subquery.mid
AND measure_instances.opinion_poll_id = subquery.oid
RETURNING total_score;
Is this canonical sql? Is there anything I should be aware of with this kind of CTE chaining (or otherwise)? Is there a more efficient way to achieve the same thing?
This is a bit long for a comment.
I don't understand the questions.
Why is the result an integer rather then a float?
Because measure_instances.total_score is an integer and that is what the returning clause is returning.
Why is the result not being grouped by measure instance instead being identical across all?
When I run the CTE independently, the values are 0.45. The data and logic dictate the same values.
And why is the result incorrect for any of them?
I think you mean "for all of them". In any case, the results look correct to me.
If you run this query against data in your demo:
SELECT
answer_options.weight, measure_instances.id
FROM
question_instances
INNER JOIN
answer_options ON question_instances.template_id = answer_options.question_template_id
INNER JOIN
scores ON question_instances.template_id = scores.question_template_id
INNER JOIN
measure_instances ON measure_instances.template_id=scores.measure_template_id
WHERE
measure_instances.opinion_poll_id = question_instances.opinion_poll_id
ORDER BY
2;
You will get:
| weight | id |
|--------|----|
| 0.5 | 1 |
| 0.25 | 1 |
| 0.25 | 1 |
| 0.75 | 1 |
| 0.5 | 1 |
| 0.75 | 2 |
| 0.5 | 2 |
| 0.25 | 2 |
| 0.5 | 2 |
| 0.25 | 2 |
If you calculate averages by hand, you will get:
For id=1 ==> 0.5+0.25+0.25+0.75 + 0.5 = 2.25 ==> 2.25 / 5 = 0.45
For id=2 ==> 0.75 + 0.5 + 0.25 + 0.5 + 0.25 = 2.25 ==> 2.25 / 5 = 0.45
It seems to me, that this query is working perfectly.
Please explain why these results are wrong to you, and what do you expect to get from the above data and query?

Transform database columns to rows and sum

I'm building a system where I have to find the combined price of a computer system by using the database data. The first screenshot is a build from the system table.
Systems Table
Parts Table
The different kinds are: motherboard, case, ram, cpu, graphic.
What I need is some way of turning the columns into rows and thereby summing the prices of each system.
Here is the table and content.
CREATE TABLE Component (
nome VARCHAR(30),
kind VARCHAR(10), /*cpu, ram, mainboard, cases*/
price INT,
PRIMARY KEY(nome)
);
CREATE TABLE Computer_system (
nome VARCHAR(30),
ram VARCHAR(20),
cpu VARCHAR(20),
mainboard VARCHAR(20),
cases VARCHAR(20),
gfx VARCHAR(20),
PRIMARY KEY(nome)
);
INSERT INTO Computer_system VALUES('SERVER1','D31','XEON1','LGA2011_D3_E_OGFX','CASE_A',null);
INSERT INTO Computer_system VALUES('SERVER2','D43','XEON3','LGA2011_D4_E_OGFX','CASE_A',null);
INSERT INTO Computer_system VALUES('CONSUMER1','D43','I71','LGA1150_D4_ATX_OGFX','CASE_B',null);
INSERT INTO Computer_system VALUES('GAMING1', 'D51', 'FX','AM3+_D5_ATX','BLACK_PEARL', 'NVIDIA_TITAN_BLACK_X');
INSERT INTO Computer_system VALUES('BUDGETO', 'D31', 'XEON1','LGA2011_D3_ATX','CASE_B', null);
There's a neat trick for unpivot in Postgres using UNNEST( ARRAY( ...) )
This efficiently (in one pass of the table) unpivots those multiple columns of table computer_system into multiple rows of (in this case) 3 columns: "nome", "colkind" and "colnome". An example of the unpivoted data:
| nome | colkind | colnome |
|-----------|-----------|----------------------|
| BUDGETO | ram | D31 |
| BUDGETO | gfx | (null) |
| BUDGETO | cases | CASE_B |
| BUDGETO | mainboard | LGA2011_D3_ATX |
| BUDGETO | cpu | XEON1 |
Once that data is available in that format it is simple to join to the Components table, like this:
SELECT
*
FROM (
/* this "unpivots" the source data */
SELECT
nome
, unnest(array[ram, cpu, mainboard,cases,gfx]) AS colnome
, unnest(array['ram', 'cpu', 'mainboard','cases','gfx']) AS colkind
FROM Computer_system
) unpiv
INNER JOIN Components c ON unpiv.colnome = c.nome AND unpiv.colkind = c.kind
;
From here it is simple to arrive at this result:
| nome | sum_price |
|-----------|-----------|
| BUDGETO | 291 |
| GAMING1 | 515 |
| CONSUMER1 | 292 |
| SERVER1 | 285 |
| SERVER2 | 289 |
using:
SELECT
unpiv.nome, sum(c.price) sum_price
FROM (
/* this "unpivots" the source data */
SELECT
nome
, unnest(array[ram, cpu, mainboard,cases,gfx]) AS colnome
, unnest(array['ram', 'cpu', 'mainboard','cases','gfx']) AS colkind
FROM Computer_system
) unpiv
INNER JOIN Components c ON unpiv.colnome = c.nome AND unpiv.colkind = c.kind
GROUP BY
unpiv.nome
;
See this SQLfiddle demo & please take note of the execution plan
QUERY PLAN
HashAggregate (cost=487.00..488.00 rows=100 width=82)
-> Hash Join (cost=23.50..486.50 rows=100 width=82)
Hash Cond: ((((unnest(ARRAY[computer_system.ram, computer_system.cpu, computer_system.mainboard, computer_system.cases, computer_system.gfx])))::text = (c.nome)::text) AND ((unnest('{ram,cpu,mainboard,cases,gfx}'::text[])) = (c.kind)::text))
-> Seq Scan on computer_system (cost=0.00..112.00 rows=20000 width=368)
-> Hash (cost=15.40..15.40 rows=540 width=120)
-> Seq Scan on components c (cost=0.00..15.40 rows=540 width=120)
I Think you need break down your table design into 3 table, there are Component, Computer_System and Computer_component. Below are the field list:
Computer_System -> computer_id and name
Component -> nome_component, kind, price
Computer_Component -> computer_id, nome_component. With that table, you can sum the total price for each computer_id by join the Computer_System a JOIN Computer Component b ON a.computer_id = b.Computer id JOIN Component c ON b.nome_component = c.nome_component
you can do it simply with joining Computer_system table with Component for each kind like below query:
select c.nome as name,
(coalesce(ram.price,0)
+coalesce(cpu.price,0)
+coalesce(+mainboard.price,0)
+coalesce(cases.price,0)
+coalesce(gfx.price,0)) as price
from Computer_system c
left join Components ram on c.ram=ram.nome
left join Components cpu on c.cpu=cpu.nome
left join Components mainboard on c.mainboard=mainboard.nome
left join Components cases on c.cases=cases.nome
left join Components gfx on c.gfx=gfx.nome
SQLFIDDLE DEMO
This is tricky to do because your table structures aren't suited to this type of query. Also, it is not flexible in case you want more than one gfx in a build.
Here is my suggested answer:
select sum(price)
from components
where nome in (
select ram from computer_system where nome = 'GAMING1'
UNION ALL
select cpu from computer_system where nome = 'GAMING1'
UNION ALL
select mainboard from computer_system where nome = 'GAMING1'
UNION ALL
select cases from computer_system where nome = 'GAMING1'
UNION ALL
select gfx from computer_system where nome = 'GAMING1'
)
;
And here it is in a working fiddle: http://sqlfiddle.com/#!15/228d7/8
If I restructured the tables, I would make them something like in this fiddle: http://sqlfiddle.com/#!15/f4ed06/1 with and extra parts_list table:
CREATE TABLE parts_list (
system_nome VARCHAR(30),
component_kind VARCHAR(10),
component_nome VARCHAR(30),
PRIMARY KEY (system_nome, component_kind, component_nome)
);
and your query for the cost of the GAMING1 system becomes much simpler:
select sum(price)
from components as c
inner join parts_list as PL ON c.kind = pl.component_kind and c.nome = pl.component_nome
where pl.system_nome = 'GAMING1'
;

Selecting a number of related records into a result row

I am currently writing an export function for an MS-Access database and i am not quite sure how to write a query that gives me the results that i want.
What i am trying to do is the following:
Let's say i have a table Error and there is a many-to-many relationship to the table Cause, modeled by the table ErrorCause. Currently i have a query similar to this (simplified, the original also goes one relationship further):
select Error.ID, Cause.ID
from ((Error inner join ErrorCauses on Error.ID = ErrorCauses.Error)
left join Cause on ErrorCauses.Cause = Cause.ID)
I get something like this:
Error | Cause
-------------
12345 | 12
12345 | 23
67890 | 23
67890 | 34
But i need to select the IDs of the first, say, 3 Causes for each error (even if those are empty), so that it looks like this:
Error | Cause1 | Cause2 | Cause3
--------------------------------
12345 | 12 | 23 |
67890 | 23 | 34 |
Is there any way to do this in a single query?
Like selecting the Top 3 and then flattening this into the resulting row?
Thanks in advance for any pointers.
Your requirement is for a specific number of causes--3. This makes it possible and manageable to get three different causes on the same row by doing a three-way join on the same subquery.
First, let's define your error-and-cause query as a straight-up Access query (a QueryDef object, if you want to be technical).
qryErrorCauseInfo:
select
Error.ID as ErrorID
, Cause.ID as CauseID
from (Error
inner join ErrorCauses
on Error.ID = ErrorCauses.Error)
left outer join Cause
on ErrorCauses.Cause = Cause.ID
By the way, I feel that the above left join should really be an inner join, for the reason I mentioned in my comment.
Next, let's do a three-way join to get possible combinations of causes in rows:
qryTotalCause:
select distinct
*
, iif(Cause1 is null, 0, 1)
+ iif(Cause2 is null, 0, 1)
+ iif(Cause3 is null, 0, 1) as TotalCause
from (
select
eci1.ErrorID
, eci1.CauseID as Cause1
, iif(eci2.CauseID = Cause1, null, eci2.CauseID) as Cause2
, iif(
eci3.CauseID = Cause1 or eci3.CauseID = Cause2
, null
, eci3.CauseID
) as Cause3
from (qryErrorCauseInfo as eci1
left outer join qryErrorCauseInfo as eci2
on eci1.ErrorID = eci2.ErrorID)
left outer join qryErrorCauseInfo as eci3
on eci2.ErrorID = eci3.ErrorID
) as sq
where (
Cause1 < Cause2
and Cause2 < Cause3
) or (
Cause1 < Cause2
and Cause3 is null
) or (
Cause2 is null
and Cause3 is null
) or (
Cause1 is null
and Cause2 is null
and Cause3 is null
)
Finally, we need a correlated subquery to select, for each error, the one row with the highest number of causes (the rest of the rows are simply different permutations of the same causes):
select
ErrorID
, Cause1
, Cause2
, Cause3
from qryTotalCause as tc1
where tc1.TotalCause = (
select max(tc2.TotalCause)
from qryTotalCause as tc2
where tc1.ErrorID = tc2.ErrorID
)
Simple! (Not :-)