Multiplying values from two Access tables - sql

I have two tables as below in Accecss 2007.
Town Table
----------
TownName | FlatCount | DetachedCount | SemiCount
A | 5 | 3 | 4
B | 2 | 6 | 3
Cost Table
----------
Prop | PCost
Flat | 10
Detached | 20
Semi | 30
I would like to get an output like below by multiplying the Count from the Town table with the corresponding PCost in the Cost table. FlatCost = Town.FlatCount * Cost.PCost for a flat.
Results
-------
Town | FlatCount | FlatCost | DetachedCount | DetachedCost | .....
A | 5 | 50 | 3 | 60 |
B | 2 | 20 | 6 | 120 |
I have tried to do this by using IIF, but not sure how to get PCost for each property type within the IIF clause.
Thanks

Looks like you are mixing data and meta data e.g. the data value Flat in table Cost becomes metadata value (column name) FlatCount in table Town. This is not a good idea and is probably why you are having difficulties writing what should be a simply query.
Restructure your Town table so that it has columns TownName, Prop and PCount. And remember that most bad SQL DML is caused by bad SQL DDL ;)

You could use a subquery to retrieve the cost of an item:
select TownName
, FlatCount
, FlatCount * (select PCost from Cost where Prop = 'Flat') as FlatCost
, DetachedCount
, DetachedCount * (select PCost from Cost where Prop = 'Detached')
as DetachedCost
, ...
from Town

You have to cross join the tables. Then, for good values, put PCost in the multiplication, else, put 0.
You can then do a SUM using a Group by :
SELECT t.Town,
t.FlatCount,
SUM(t.FlatCount * IIF(c.Prop = 'Flat', c.PCost, 0)) AS FlatCost,
t.DetachedCount,
SUM(t.DetachedCount * IIF(c.Prop = 'Detached', c.PCost, 0)) AS DetachedCost,
FROM Town t, Cost c
GROUP BY t.Town, t.FlatCount, t.DetachedCount

Related

How to generate a sequence of ID's based on mapping tables and values from the forms in MS-Access (Sql)?

I want to generate ID's based on the form values in MS-Access. And then for each ID generated, create a group of ID's by adding another 4 digits in the end based on a Mapping Table, representing different octets for different time points (12 ID's based on the Initial ID and the mapping Table).
For example, if the ID generated based on form values is 123456, I want to add another four digits and create a group of ID's, say from a mapping table. Like,
123456**1111**
123456**1112**
123456**1113**
and so on.
So far each primary ID, I am slapping on four digits at the end and generating a group of 12 ID's.
I am a beginner in Access and I have tried some code:
UPDATE Table1 SET GenID = UPDATE Table1 SET Table1.GenID = t1 (SELECT Map.V FROM MAP as t1)
However, I get a error that Access does not recognize Map as a valid Field or expression. I am able to break down the problem into this. But could not find a way further and design a query.
Sample Data: (The short_ID and Long_ID tables, uses the mapping tables below each of them as shown.)
Short ID Table:
----------------------------------------------------
ID | Subject_ID | Organ_Type | Category | Short_ID
-----------------------------------------------------
1 | 100 | Kidney | A | 100200300
-----------------------------------------------------
2 | 400 | Heart | B | 400500600
Mapping Tables for Short ID:
Map1 for Table1:
---------------------
Map_from | Map_to |
---------------------
Kidney | 200 |
Heart | 500 |
---------------------
Map2 for Table1:
-----------------------------
Map_cat_from | Map_cat_to |
-----------------------------
A | 300 |
B | 600 |
-----------------------------
Long ID Table:(shown here are just examples for 2 time points rather than 12)
---------------------------------------------------
Subject_ID | Short_ID | Long_ID Timepoint |
---------------------------------------------------
100 | 100200300 | 1002003000001 |
---------------------------------------------------
100 | 100200300 | 1002003000002 |
---------------------------------------------------
400 | 400500600 | 4005006000001 |
---------------------------------------------------
400 | 400500600 | 4005006000002
Timepoint Map for Long ID Table:
------------------------------
Timepoint | Value_to_append |
------------------------------
1 | 0001 |
------------------------------
2 | 0002 |
I need to generate these short and long ID's from the mapping tables directly when input is given in the form. (Category, Organ_Type, Subject_ID)
tldr
generate id from mapping table and form values (id creation)
add four digits at the end and create a group of 12 id's (long id creation) based on a mapping table (which has the 12 four digits that is to be appended in the end)
First, create a query, QShortID:
SELECT
Table1.ID,
Table1.Subject_ID,
Table1.Organ_Type,
Table1.Category,
[Subject_ID] & [Map_to] & [Map_cat_to] AS Short_ID
FROM
(Table1
INNER JOIN
Map1
ON Table1.Organ_Type = Map1.Map_from)
INNER JOIN
Map2
ON Table1.Category = Map2.Map_cat_from;
Output:
Next, create a query, Dozen, that will return 12 rows:
SELECT DISTINCT
Abs([id] Mod 12) AS N
FROM
MSysObjects;
Finally, create a Cartesian (multiplying) query, QLongID:
SELECT Table1.ID, Table1.Subject_ID, Table1.Organ_Type, Table1.Category, [Subject_ID] & [Map_to] & [Map_cat_to] AS Short_ID
FROM (Table1 INNER JOIN Map1 ON Table1.Organ_Type = Map1.Map_from) INNER JOIN Map2 ON Table1.Category = Map2.Map_cat_from;
SELECT
QShortID.Subject_ID,
QShortID.Short_ID,
[Short_ID] & Format([N] + 1, "0000") AS Long_ID
FROM
QShortID,
Dozen
ORDER BY
[Short_ID] & Format([N] + 1, "0000");
Output:
Edit:
To use the timepoint mapping, use:
SELECT
QShortID.Subject_ID,
QShortID.Short_ID,
[Short_ID] & [Value_to_append] AS Long_ID
FROM
QShortID,
TimepointMap
ORDER BY
[Short_ID] & [Value_to_append];
Output:

Select the difference of two consecutive columns

I have a table car that looks like this:
| mileage | carid |
------------------
| 30 | 1 |
| 50 | 1 |
| 100 | 1 |
| 0 | 2 |
| 70 | 2 |
I would like to get the average difference for each car. So for example for car 1 I would like to get ((50-30)+(100-50))/2 = 35. So I created the following query
SELECT AVG(diff),carid FROM (
SELECT (mileage-
(SELECT Max(mileage) FROM car Where mileage<mileage AND carid=carid GROUP BY carid))
AS diff,carid
FROM car GROUP BY carid)
But this doesn't work as I'm not able to use current row for the other column. And I'm quite clueless on how to actually solve this in a different way.
So how would I be able to obtain the value of the next row somehow?
The average difference is the maximum minus he minimum divided by one less than the count (you can do the arithmetic to convince yourself this is true).
Hence:
select carid,
( (max(mileage) - min(mileage)) / nullif(count(*) - 1, 0)) as avg_diff
from cars
group by carid;

how to get daily profit from sql table

I'm stucking for a solution at the problem of finding daily profits from db (ms access) table. The difference wrt other tips I found online is that I don't have in the table a field "Price" and one "Cost", but a field "Type" which distinguish if it is a revenue "S" or a cost "C"
this is the table "Record"
| Date | Price | Quantity | Type |
-----------------------------------
|01/02 | 20 | 2 | C |
|01/02 | 10 | 1 | S |
|01/02 | 3 | 10 | S |
|01/02 | 5 | 2 | C |
|03/04 | 12 | 3 | C |
|03/03 | 200 | 1 | S |
|03/03 | 120 | 2 | C |
So far I tried different solutions like:
SELECT
(SELECT SUM (RS.Price* RS.Quantity)
FROM Record RS WHERE RS.Type='S' GROUP BY RS.Data
) as totalSales,
(SELECT SUM (RC.Price*RC.Quantity)
FROM Record RC WHERE RC.Type='C' GROUP BY RC.Date
) as totalLosses,
ROUND(totalSales-totaleLosses,2) as NetTotal,
R.Date
FROM RECORD R";
in my mind it could work but obviously it doesn't
and
SELECT RC.Data, ROUND(SUM (RC.Price*RC.QuantitY),2) as DailyLoss
INTO #DailyLosses
FROM Record RC
WHERE RC.Type='C' GROUP BY RC.Date
SELECT RS.Date, ROUND(SUM (RS.Price*RS.Quantity),2) as DailyRevenue
INTO #DailyRevenues
FROM Record RS
WHERE RS.Type='S'GROUP BY RS.Date
SELECT Date, DailyRevenue - DailyLoss as DailyProfit
FROM #DailyLosses dlos, #DailyRevenues drev
WHERE dlos.Date = drev.Date";
My problem beyond the correct syntax is the approach to this kind of problem
You can use grouping and conditional summing. Try this:
SELECT data.Date, data.Income - data.Cost as Profit
FROM (
SELECT Record.Date as Date,
SUM(IIF(Record.Type = 'S', Record.Price * Record.Quantity, 0)) as Income,
SUM(IIF(Record.Type = 'C', Record.Price * Record.Quantity, 0)) as Cost,
FROM Record
GROUP BY Record.Date
) data
In this case you first create a sub-query to get separate fields for Income and Cost, and then your outer query uses subtraction to get actual profit.

Postgresql : Alternative to joining the same table multiple times

If i have two tables entry and entry_metadata, with the entry_metadata as a description table for the entry referenced by entry_id and a variable.
If i have this :
entry
id | name |
-------------
1 | entry1 |
2 | entry2 |
3 | entry3 |
entry_metadata
id | entry_id | variable | value
1 | 1 | width | 10
2 | 1 | height | 5
3 | 2 | width | 8
4 | 2 | height | 7
5 | ... | .... | ..
and i'm getting the table :
id | name | width | height| ... | ...
-----------------------------------------
1 | entry1 | 10 | 5 |
2 | entry2 | 8 | 7 |
3 | entry3 | .. | .. |
by the sql :
select e.name, em.width, emr.height
from
public.entry e
left join
public.entry_metadata em
on
em.entry_id = e.id and em.variable = 'width'
left join
public.entry_metadata emr
on
emr.entry_id = e.id and emr.variable = 'height'
The query above works. But as I add more variables to get the values (the entry_metadata table includes a large variety of variables) from the entry metadata. The query gets really really slow. every join I do slows down the execution greatly. Is there a way to get around this?
You can also do this with conditional aggregation:
select id, name,
max(case when variable = 'width' then value end) as width,
max(case when variable = 'height' then value end) as height
from public.entry_metadata em
group by id, name;
Adding additional columns is just adding more aggregation functions.
Just use subselects for this:
SELECT
e.id,
e.name,
(SELECT em.value FROM public.entry_metadata em WHERE em.entry_id = e.id AND em.variable = 'width') AS width,
(SELECT em.value FROM public.entry_metadata em WHERE em.entry_id = e.id AND em.variable = 'height') AS height
FROM
public.entry e
So for each new variable you just need to add one more subselect.
Is there a way to get around this?
Yes, replace entry_metadata table with addtional column in entry (possible solutions are hstore or jsonb) with key - value storage of entry metadata.
Btw. your tables represents well known controversial database desing pattern known as "Entity Attribute Value".

SQL SELECT only rows where a max value is present, and the corresponding ID from another linked table

I have a simple Parts database which I'd like to use for calculating costs of assemblies, and I need to keep a cost history, so that I can update the costs for parts without the update affecting historic data.
So far I have the info stored in 2 tables:
tblPart:
PartID | PartName
1 | Foo
2 | Bar
3 | Foobar
tblPartCostHistory
PartCostHistoryID | PartID | Revision | Cost
1 | 1 | 1 | £1.00
2 | 1 | 2 | £1.20
3 | 2 | 1 | £3.00
4 | 3 | 1 | £2.20
5 | 3 | 2 | £2.05
What I want to end up with is just the PartID for each part, and the PartCostHistoryID where the revision number is highest, so this:
PartID | PartCostHistoryID
1 | 2
2 | 3
3 | 5
I've had a look at some of the other threads on here and I can't quite get it. I can manage to get the PartID along with the highest Revision number, but if I try to then do anything with the PartCostHistoryID I end up with multiple PartCostHistoryIDs per part.
I'm using MS Access 2007.
Many thanks.
Mihai's (very concise) answer will work assuming that the order of both
[PartCostHistoryID] and
[Revision] for each [PartID]
are always ascending.
A solution that does not rely on that assumption would be
SELECT
tblPartCostHistory.PartID,
tblPartCostHistory.PartCostHistoryID
FROM
tblPartCostHistory
INNER JOIN
(
SELECT
PartID,
MAX(Revision) AS MaxOfRevision
FROM tblPartCostHistory
GROUP BY PartID
) AS max
ON max.PartID = tblPartCostHistory.PartID
AND max.MaxOfRevision = tblPartCostHistory.Revision
SELECT PartID,MAX(PartCostHistoryID) FROM table GROUP BY PartID
Here is query
select PartCostHistoryId, PartId from tblCost
where PartCostHistoryId in
(select PartCostHistoryId from
(select * from tblCost as tbl order by Revision desc) as tbl1
group by PartId
)
Here is SQL Fiddle http://sqlfiddle.com/#!2/19c2d/12