I have three tables namely the class_record, class_violation, violation.
The table class_record has these columns and data:
Class Violation CR No. | Class ID
000-000 | A30-000
000-001 | A30-000
The table class_violation has these columns and data:
Class Violation CR No. | Violation ID
000-000 | 2
000-000 | 1
000-001 | 2
000-001 | 4
000-001 | 3
The table violation has these columns and data:
Violation ID | First Amount | Second Amount
1 | 1000 | 2000
2 | 200 | 400
3 | 500 | 1000
4 | 500 | 1000
The table class_record contains the information of the class record.
The table class_violation is the table that contains what are the violations that is committed. And lastly, the table violation contains the information about the violations.
If the violation is committed twice, the second amount will be triggered instead of the first amount. As you can see on table class_violation, on Violation ID column, number 2 violation id is committed twice. The second amount of the must be the charged amount instead of the first amount. So the total charged amount will be the first amount plus the second amount if committed twice. My question is that how do I get the get the second amount instead of the first amount and get its total amount of the violations committed? So far here is my SQL query but is terribly wrong:
SELECT SUM(`First Amount`)
FROM violation
WHERE `Violation ID`
IN (SELECT `Violation ID` FROM class_violation
WHERE `Class Violation No.`
IN (SELECT `Class Violation CR No.`
FROM class_record WHERE `Class ID` = 'A30-000'))
Please help me. Sorry for my english. The result of the query must be:
SUM
2600
Here is my sqlfiddle link: http://sqlfiddle.com/#!2/2712a
See this query:
SELECT class_record.ClassID,SUM(A.VioAmount),GROUP_CONCAT(A.VioAmount)
FROM class_record
INNER JOIN (SELECT class_record.ClassID, class_record.CR_No, IF(COUNT(violation.ViolationID)=1, SUM(FAmount),(FAmount+SAmount)) AS VioAmount
FROM violation
INNER JOIN class_violation ON violation.ViolationID = class_violation.ViolationID
INNER JOIN class_record ON class_violation.CR_No = class_record.CR_No
WHERE ClassID = 'A30-000'
GROUP BY violation.ViolationID, class_record.ClassID) A ON A.ClassID = class_record.ClassID
AND A.CR_No = class_record.CR_No
GROUP BY class_record.ClassID
You can check the SQLFiddle too at http://sqlfiddle.com/#!2/0d855b/35
Now I am taking the values in a separate Select and then JOINing it with another to obtain the SUM. Hope this solves your problem. All the Best.
You need return column "Second amount" if count(Violation ID)=2 and "First amount" if count(Violation ID)=1?
If I understand your question, look for "CASE" operator.
Related
Please note that I'm an absolute n00b in MySQL but somehow I managed to build some (for me) complex queries that work as they should. My main problem now is that for a many of the queries we're working on:
The querie is becoming too big and very hard to see through.
The same subqueries get repeated many times and that is adding to the complexity (and probably to the time needed to process the query).
We want to further expand this query but we are reaching a point where we can no longer oversee what we are doing. I've added one of these subqueries at the end of this post, just as an example.
!! You can fast foward to the Problem section if you want to skip the details below. I think the question can be answered also without the additional info.
What we want to do
Create a MySQL query that calculates purchase orders and forecasts for a given supplier based on:
Sales history in a given period (past [x] months = interval)
Current stock
Items already in backorder (from supplier)
Reserved items (for customers)
Supplier ID
I've added an example of a subquery at the bottom of this message. We're showing just this part to keep things simple for now. The output of the subquery is:
Part number
Units sold
Units sold (outliers removed)
Units sold per month (outliers removed)
Number of invoices with the part number in the period (interval)
It works quite OK for us, although I'm sure it can be optimised. It removes outliers from the sales history (e.g. one customer that orders 50 pcs of one product in one order). Unfortunately it can only remove outliers with substantial data, so if the first order happens to be 50 pcs then it is not considered an outlier. For that reason we take the amount of invoices into account in the main query. The amount of invoices has to exceed a certain number otherwise the system wil revert to a fixed value of "maximum stock" for that product.
As mentioned this is only a small part of the complete query and we want to expand it even further (so that it takes into account the "sales history" of parts that where used in assembled products).
For example if we were to build and sell cars, and we want to place an
order with our tyre supplier, the query calculates the amount of tyres we need to order based on the sales history of the various car models (while also taking into account the stock of the cars, reserved cars and stock of the tyres).
Problem
The query is becomming massive and incomprehensible. We are repeating the same subqueries many times which to us seems highly inefficient and it is the main cause why the query is becomming so bulky.
What we have tried
(Please note that we are on MySQL 5.5.33. We will update our server soon but for now we are limited to this version.)
Create a VIEW from the subqueries.
The main issue here is that we can't execute the view with parameters like supplier_id and interval period. Our subquery calculates the sum of the sold items for a given supplier within the given period. So even if we would build the VIEW so that it calculates this for ALL products from ALL suppliers we would still have the issue that we can't define the interval period after the VIEW has been executed.
A stored procedure.
Correct me if I'm wrong but as far as I know, MySQL only allows us to perform a Call on a stored procedure so we still can't run it against the parameters (period, supplier id...)
Even this workaround won't help us because we still can't run the SP against the parameters.
Using WITH at the beginning of the query
A common table expression in MySQL is a temporary result whose scope is confined to a single statement. You can refer this expression multiple times with in the statement.
The WITH clause in MySQL is used to specify a Common Table Expression, a with clause can have one or more comms-separated subclauses.
Not sure if this would be the solution because we can't test it. WITH is not supported untill MySQL version 8.0.
What now?
My last resort would be to put the mentioned subqueries in a temp table before starting the main query. This might not completely eliminate our problems but at least the main query will be more comprehensible and with less repetition of fetching the same data. Would this be our best option or have I overlooked a more efficient way to tackle this?
Many thanks for your kind replies.
SELECT
GREATEST((verkocht_sd/6*((100 + 0)/100)),0) as 'units sold p/month ',
GREATEST(ROUND((((verkocht_sd/6)*3)-voorraad+reserved-backorder),0),0) as 'Order based on units sold',
SUM(b.aantal) as 'Units sold in period',
t4.verkocht_sd as 'Units sold in period, outliers removed',
COUNT(*) as 'Number of invoices in period',
b.art_code as 'Part number'
FROM bongegs b -- Table that has all the sales records for all products
RIGHT JOIN totvrd ON (totvrd.art_code = b.art_code) -- Right Join stock data to also include items that are not in table bongegs (no sales history).
LEFT JOIN artcred ON (artcred.art_code = b.art_code) -- add supplier ID to the part numbers.
LEFT JOIN
(
SELECT
SUM(b.aantal) as verkocht_sd,
b.art_code
FROM bongegs b
RIGHT JOIN totvrd ON (totvrd.art_code = b.art_code)
LEFT JOIN artcred ON (artcred.art_code = b.art_code)
WHERE
b.bon_datum > DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
and b.bon_soort = "f" -- Selects only invoices
and artcred.vln = 1 -- 1 = Prefered supplier
and artcred.cred_nr = 9117 -- Supplier ID
and b.aantal < (select * from (SELECT AVG(b.aantal)+3*STDDEV(aantal)
FROM bongegs b
WHERE
b.bon_soort = 'f' and
b.bon_datum > DATE_SUB(CURDATE(), INTERVAL 6 MONTH)) x)
GROUP BY b.art_code
) AS t4
ON (b.art_code = t4.art_code)
WHERE
b.bon_datum > DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
and b.bon_soort = "f"
and artcred.vln = 1
and artcred.cred_nr = 9117
GROUP BY b.art_code
Bongegs | all rows from sales forms (invoices F, offers O, delivery notes V)
| art_code | bon_datum | bon_soort | aantal |
|:---------|:---------: |:---------:|:------:|
| item_1 | 2021-08-21 | f | 6 |
| item_2 | 2021-08-29 | v | 3 |
| item_6 | 2021-09-03 | o | 2 |
| item_4 | 2021-10-21 | f | 6 |
| item_1 | 2021-11-21 | o | 6 |
| item_3 | 2022-01-17 | v | 6 |
| item_1 | 2022-01-21 | o | 6 |
| item_4 | 2022-01-26 | f | 6 |
Artcred | supplier ID's
| art_code | vln | cred_nr |
|:---------|:----:|:-------:|
| item_1 | 1 | 1001 |
| item_2 | 1 | 1002 |
| item_3 | 1 | 1001 |
| item_4 | 1 | 1007 |
| item_5 | 1 | 1004 |
| item_5 | 2 | 1008 |
| item_6 | 1 | 1016 |
| item_7 | 1 | 1567 |
totvrd | stock
| art_code | voorraad | reserved | backorder |
|:---------|:---------: |:--------:|:---------:|
| item_1 | 1 | 0 | 5 |
| item_2 | 0 | 0 | 0 |
| item_3 | 88 | 0 | 0 |
| item_4 | 9 | 0 | 0 |
| item_5 | 67 | 2 | 20 |
| item_6 | 112 | 9 | 0 |
| item_7 | 65 | 0 | 0 |
| item_8 | 7 | 1 | 0 |
Now, on to the query. You have LEFT JOINs to the artcred table, but then include artcred in the WHERE clause making it an INNER JOIN (required both left and right tables) in the result. Was this intended, or are you expecting more records in the bongegs table that do NOT exist in the artcred.
Well to be honest I was not fully aware that this would essentially form an INNER JOIN but in this case it doesn't really matter. A record that exists in bongegs always exists in artcred as well (every sold product must have a supplier). That doesn't work both ways since a product can be in artcred without ever being sold.
You also have RIGHT JOIN on totvrd which implies you want every record in the TotVRD table regardless of a record in the bongegs table. Is this correct?
Yes it is intended. Otherwise only products with actual sales in the period would end up in the result and we also wanted to include products with zero sales.
One simplification:
and b.aantal < ( SELECT * from ( SELECT AVG ...
-->
and b.aantal < ( SELECT AVG ...
A personal problem: my brain hurts when I see RIGHT JOIN; please rewrite as LEFT JOIN.
Check you RIGHTs and LEFTs -- that keeps the other table's rows even if there is no match; are you expecting such NULLs? That is, it looks like they can all be plain JOINs (aka INNER JOINs).
These might help performance:
b: INDEX(bon_soort, bon_datum, aantal, art_code)
totvrd: INDEX(art_code)
artcred: INDEX(vln, cred_nr, art_code)
Is b the what you keep needing? Build a temp table:
CREATE TEMPORARY TABLE tmp_b
SELECT ...
FROM b
WHERE ...;
But if you need to use tmp_b multiple times in the same query, (and since you are not yet on MySQL 8.0), you may need to make it a non-TEMPORARY table for long enough to run the query. (If you have multiple connections building the same permanent table, there will be trouble.)
Yes, 5.5.33 is rather antique; upgrade soon.
(pre
By getting what I believe are all the pieces you had, I think this query significantly simplifies the query. Lets first start with the fact that you were trying to eliminate the outliers by selecting the standard deviation stuff as what to be excluded. Then you had the original summation of all sales also from the bongegs table.
To simplify this, I have the sub-query ONCE internal that does the summation, counts, avg, stddev of all orders (f) within the last 6 months. I also computed the divide by 6 for per-month you wanted in the top.
Since the bongegs is now all pre-aggregated ONCE, and grouped per art_code, it does not need to be done one after the other. You can use the totals directly at the top (at least I THINK is similar output without all actual data and understanding of your context).
So the primary table is the product table (Voorraad) and LEFT-JOINED to the pre-query of bongegs. This allows you to get all products regardless of those that have been sold.
Since the one aggregation prequery has the avg and stddev in it, you can simply apply an additional AND clause when joining based on the total sold being less than the avg/stddev context.
The resulting query below.
SELECT
-- appears you are looking for the highest percentage?
-- typically NOT a good idea to name columns starting with numbers,
-- but ok. Typically let interface/output name the columns to end-users
GREATEST((b.verkocht_sdperMonth * ((100 + 0)/100)),0) as 'units sold p/month',
-- appears to be the total sold divided by 6 to get monthly average over 6 months query of data
GREATEST( ROUND(
( (b.verkocht_sdperMonth * 3) - v.voorraad + v.reserved - v.backorder), 0), 0)
as 'Order based on units sold',
b.verkocht_sd as 'Units sold in period',
b.AvgStdDev as 'AvgStdDeviation',
b.NumInvoices as 'Number of invoices in period',
v.art_code as 'Part number'
FROM
-- stock, master inventory, regardless of supplier
-- get all products, even though not all may be sold
Voorraad v
-- LEFT join to pre-query of Bongegs pre-grouped by the art_code which appears
-- to be basis of all other joins, std deviation and average while at it
LEFT JOIN
(select
b.arc_code,
count(*) NumInvoices,
sum( b.aantal ) verkocht_sd,
sum( b.aantal ) / 6.0 verkocht_sdperMonth,
avg( b.aantal ) AvgSale,
AVG(b.aantal) + 3 * STDDEV( b.aantal) AvgStdDev
from
bongegs b
JOIN artcred ac
on b.art_code = ac.art_code
AND ac.vln = 1
and ac.cred_nr = 9117
where
-- only for ORDERS ('f') and within last 6 months
b.bon_soort = 'f'
AND b.bon_datum > DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
group by
b.arc_code ) b
-- result is one entry per arc_code, thus preventing any Cartesian product
ON v.art_code = b.art_code
GROUP BY
v.art_code
I'm post-processing traces for two different kinds of events, where the data is stored in table A and B. Both tables have an producer ID and a time index value. While the same producer can trigger a record in both tables, the time when the different events occur are independent, and much more frequent for table B.
I want to update table A such that, for every row in table A, a column value from table B is taken for the most recent row in table B for the same producer.
Example mappings between two tables:
Here is a simplified example with just one producer in both tables. The goal is not to get the oldest entry in table B, but rather the most recent entry in table B relative to a row in table A. I'm showing B.tIdx < A.tIdx in this example, but <= is just as good for my purposes; just a detail.
Table A Table B
+----+------+----------------------+ +------+------+-------+
| ID | tIdx | NEW value SET FROM B | | ID | tIdx | value |
+----+------+----------------------+ +------+------+-------+
| 1 | 2 | 12.5 | | 1 | 1 | 12.5 |
| 1 | 4 | 4.3 | | 1 | 2 | 9.0 |
+----+------+----------------------+ | 1 | 3 | 4.3 |
| 1 | 4 | 7.8 |
| 1 | 5 | 6.2 |
+------+------+-------+
The actual tables have thousands of different IDs, millions of rows, and nearly as many distinct time index values as rows. I'm having trouble to come up with an UPDATE that doesn't take days to complete.
The following UPDATE works, but executes far too slowly; it starts off at a rate of 100s of updates/s, but soon slows to roughly 5 updates/s.
UPDATE A AS x SET value =
(SELECT value
FROM B AS y
WHERE x.ID = y.ID AND x.tIdx > y.tIdx
ORDER BY y.tIdx DESC
LIMIT 1);
I've tried creating indexes for ID and tIdx separately, but also multi-column indexes with both orders (ID,tIdx) and (tIdx,ID). But even when the multi-column indexes exist, EXPLAIN shows that it only ever indexes on ID or tIdx, but not both together.
I was wondering if the solution is to create nested SELECTs, to first get a temporary table with a particular ID, and then find the 1 row in table B that will meet the time constraint for each tIdx for that particular ID. The following SELECT, with hardcoded ID and tIdx, works and is very fast, completing in 0.00 sec.
SELECT value, ID, tIdx
FROM (
SELECT value, ID, tIdx
FROM B
WHERE ID = 5216
) y
WHERE tIdx < 1253707
ORDER BY tIdx DESC LIMIT 1;
I'd like to incorporate this into an UPDATE somehow, but replace the hardcoded ID and tIdx with the ID,tIdx pair for each row in A.
Or try any other suggestion for a more efficient UPDATE statement.
This is my first post to stackoverflow. Sincere apologizes in advance if I have violated any etiquette.
Update with Inner Join should do it, but it's going to get nasty to do this.
Update a INNER JOIN
(Select b.ID, maxb.atIdx, b.value
From b INNER JOIN (Select a.ID, a.tIdx as atIdx, max(b.tIdx) as bigb
From b INNER JOIN a
ON b.ID=a.ID
Where b.tIdx<=a.tIdx
Group By a.ID,a.tIdx) maxb
ON b.ID=maxb.ID and b.tIdx=maxb.bigb
) bestb ON a.ID=bestb.ID and a.tIdx=bestb.atIdx
Set a.value=bestb.value
To explain this it's best to start with the innermost SQL and work your way to the outermost UPDATE. To start, we need to join every record in table A to every record in table B for each ID. We can filter out the B records that are too recent and summarize that result for each table A record. That leaves us with the tIdx of the B table whose value goes into A for every record key in A. So then we join that to the B table to select the values to update, preserving the A-table's keys. That result is joined back to A to perform the update.
You'll have to see whether this is fast enough for you - I'm worried that this accesses the B table twice and the inner query creates A LOT of join combinations. I would pull out that inner query and see how long it runs by itself. On the positive side, they are all very simple, straightforward queries and they are connected by Inner Joins so there is some opportunity for efficiency in the query optimizer. I think indexes on a(ID,TIdx) [fast lookup to get the Update row] and b(ID) would be useful here.
One thing you can try is lead() to see if that helps the performance:
UPDATE A JOIN
(SELECT b.*,
LEAD(tIDx) OVER (PARTITION BY id order by tIDx) as next_tIDx
FROM b
) b
ON a.id = b.id AND
a.tIDx >= b.tIDx AND
(b.next_tIDx IS NULL or a.tIDx < b.next_tIDx)
SET a.value = b.value;
And for this you want an index on b(id, tidx).
This question relates to one I raised here SQL help in finding missing operations on the Sparx website. They suggested I try StackOverflow so here I am. I had tried to do a lot of research on the problem before posting on the Sparx website. I have slightly tweaked it to make it more of a general SQL issue than fixed to the tool that I am using.
I am not an SQL guru so please be kind to me!
The situation
I have 2 tables that has the following elements
Table t_object
--------------
Object_ID AutoNumber
Object_Type Text
Name Text
ParentID Number
Table t_operation
-----------------
OperationID AutoNumber
Object_ID Number
Name Text
The table t_object contains lots of different types of items. The Object_ID is the unique key. The items that I am interested are where Object_Type = 'class' OR 'activity' OR 'activityparameter'. The Name is the name of the item. The ParentID is only applicable to 'activityparameter' items and is the Object_ID of the 'activity' that the 'activityparameter' belongs to.
The table t_operation contains all operations belonging to a class. The OperationID is the unique key. The Object_ID is how this operation is linked to its class in t_object.
The problem
In our system all classes have an equally named activity and all operations belonging to a class have an equally named activityparameter belonging to the activity.
I am trying to find erroneous entries where operation MyOp in class MyClass does not have an equally named activityparameter MyOp in activity MyClass.
Using the test data below
t_object
+-----------+-------------------+-------+----------+
| Object_ID | Object_Type | Name | ParentID |
+-----------+-------------------+-------+----------+
| 1 | Class | c1 | 0 |
| 2 | Class | c2 | 0 |
| 3 | Activity | c1 | 0 |
| 4 | Activity | c2 | 0 |
| 5 | ActivityParameter | MyOp1 | 3 |
| 6 | ActivityParameter | MyOp2 | 3 |
| 7 | ActivityParameter | MyOp3 | 3 |
| 8 | ActivityParameter | MyOp1 | 4 |
| 9 | ActivityParameter | MyOp2 | 4 |
+-----------+-------------------+-------+----------+
t_operation
+-------------+-----------+-------+
| OperationID | Object_ID | Name |
+-------------+-----------+-------+
| 1 | 1 | MyOp1 |
| 2 | 1 | MyOp2 |
| 3 | 2 | MyOp1 |
| 4 | 2 | MyOp2 |
| 5 | 2 | MyOp3 |
| 6 | 2 | MyOp4 |
+-------------+-----------+-------+
The above tables represent the following
Operation c1::MyOp1 (from class c1)
Operation c1::MyOp2 (from class c1)
Operation c2::MyOp1 (from class c2)
Operation c2::MyOp2 (from class c2)
Operation c2::MyOp3 (from class c2)
Operation c2::MyOp4 (from class c2)
Activity parameter c1::MyOp1 (from activity c1)
Activity parameter c1::MyOp2 (from activity c1)
Activity parameter c1::MyOp3 (from activity c1)
Activity parameter c2::MyOp1 (from activity c2)
Activity parameter c2::MyOp2 (from activity c2)
We can see the following errors
Operation c2::MyOp3 has no equivalent activity parameter
Operation c2::MyOp4 has no equivalent activity parameter
Activity parameter c1::MyOp3 has no equivalent operation
For the purpose of this question I am not interested in the final error. When I get the SQL query for "operation class::operation has no equivalent activity parameter" then I will have the logic to do the reverse.
I tried the SQL query (I am using Access and also MySQL). Note, text searches are case insensitive. The 2 IDs at the end of the SELECT should be equal if the activity parameter belongs to the mentioned activity. If they are different then the returned activity parameter belongs to a different activity. This acts as a quick cross-check.
SELECT o_class.name, o_operation.name, o_activity.name, o_actparam.name, o_activity.object_ID AS "activity ID", o_actparam.parentID AS "belongs to activity ID"
FROM
(((
t_object o_class
INNER JOIN t_object o_activity ON
( o_activity.name = o_class.name
AND
o_class.object_type = 'class'
AND
o_activity.object_type = 'activity'
)
)
INNER JOIN t_operation o_operation ON o_operation.object_id = o_class.object_id)
LEFT JOIN t_object o_actparam ON
( o_actparam.name = o_operation.name
AND
o_actparam.object_type = 'activityparameter'
AND
o_actparam.parentid = o_activity.object_id
)
)
WHERE
o_actparam.name is NULL
ORDER BY
o_class.name, o_operation.name, o_activity.name, o_actparam.name, o_activity.object_ID, o_actparam.parentID
The above aims to get a class, then an activity with the same name, then all operations belonging to the class, then for each operation try and find an activity parameter in this activity with the same name. Any that didn't match should return NULL (since it is a LEFT JOIN) and so the WHERE statement shows the operations that didn't have a related activityparameter, i.e. the errors.
The above does not work; I get a "JOIN expression not supported".
If I take out the "o_actparam.parentid = o_activity.object_id" then it returns no results at all. This is clearly wrong. I believe this is because the LEFT JOIN matches on the first expression, i.e. "o_actparam.name = o_operation.name", then applies any other expressions to that result. So it returns 10 rows (one NULL for c2::MyOp4) but the 2nd expression (o_actparam.object_type = 'activityparameter') then throws away the NULL (c2::MyOp4). Then all 9 results are thrown away by the WHERE clause.
If I change the LEFT JOIN to
LEFT JOIN t_object o_actparam ON
( o_actparam.name = o_operation.name
AND
( o_actparam.object_type = 'activityparameter'
OR
o_actparam.object_type is NULL
)
)
)
then I get the result
c2::MyOp4
It has failed to find c2::MyOp3. This is because operation c2::MyOp3 matches activityparmater c1::MyOp3 (same activity parameter name even though it belongs to the wrong class/activity c1). The LEFT JOIN comparison ignores the class/activity. Remember, if I put the 'o_actparam.parentid = o_activity.object_id' check then I get "JOIN expression not supported".
If I change the WHERE (and keep the above LEFT JOIN) to
WHERE
o_actparam.parentid <> o_activity.object_id
OR
o_actparam.parentid is NULL
then I get the result
c1::MyOp1
c1::MyOp2
c2::MyOp1
c2::MyOp2
c2::MyOp3
c2::MyOp4
It is now finding lots of wrong items since the same activityparameter name exists in different activities. The WHERE is too late to throw away items, however, putting the parentid in the LEFT JOIN gave me the "JOIN expression is not supported" error.
In my research I noticed that I could concatenate the expressions in an ON clause, e.g.
LEFT JOIN t_object o_actparam ON
o_actparam.name + o_actparam.object_type + o_actparam.parentid = o_operation.name + 'activityparameter' + o_activity.object_id
)
The idea here is that if the LEFT JOIN works on the first expression then filters on any subsequent ones then putting them all as one would do what I wanted. It also has the benefit of not requiring "OR xxx is NULL" all over the place. It also reinforces what I am looking for, the combination of name, object type and parent. Naturally the above didn't work (or I wouldn't be asking here); it still gave the same "JOIN expression not supported". Again, removing the "parentID" aspect gave wrong results.
I hope I have given a detailed situation, test cases, expected answers, my reasonings and my research to show this is not just a "I couldn't be bothered to work it out myself, please help". I have spent days googling and trying out SQL but, as I said, I am not an SQL expert.
Is someone able to help me here?
Thanks
Darren
I have found the answer so am posting it here in case someone finds it useful. Thanks Sandra for your help.
The answer is that my problem LEFT JOIN was trying to access entries in earlier tables but it couldn't see them because I referred to 2 different tables. I am not an SQL expert so I don't know the correct terminology or exactly why.
Anyway, the solution is to place all previous tables into a subquery so that it then gets a new single table name.
Note, since I am using Access, I have been told that the parentheses after the ON part of the LEFT JOIN are essential since I am joining on a constant value ('activityparameter').
The changes needed were to insert
(SELECT * FROM
before the first 2 tables. Note that this required me to break the single line with 3 parentheses into 2 lines as the SELECT needed to go after the first parenthesis.
After that add an extra
) AS q1
after the 2 tables. In my final answer below I've indented this whole section and put a blank line before and after for clarity.
Then in my LEFT JOIN I refer to the old tables by adding q1. at the start of each reference.
The following query works perfectly and gives the results that I expected.
SELECT o_class.name, o_operation.name, o_activity.name, o_actparam.name, o_activity.object_ID AS "activity ID", o_actparam.parentID AS "belongs to activity ID"
FROM
(
(SELECT * FROM
((
t_object o_class
INNER JOIN t_object o_activity ON
( o_activity.name = o_class.name
AND
o_class.object_type = 'class'
AND
o_activity.object_type = 'activity'
)
)
INNER JOIN t_operation o_operation ON o_operation.object_id = o_class.object_id)
) AS q1
LEFT JOIN t_object o_actparam ON
( o_actparam.name = q1.o_operation.name
AND
o_actparam.object_type = 'activityparameter'
AND
o_actparam.parentid = q1.o_activity.object_id
)
)
WHERE
o_actparam.name is NULL
ORDER BY
o_class.name, o_operation.name, o_activity.name, o_actparam.name, o_activity.object_ID, o_actparam.parentID
I asked a similar question earlier today, but I've run into another issue that I need assistance with.
I have a logging system that scans a server and catalogs every user that's online at that given moment. Here is how my table looks like:
-----------------
| ab_logs |
-----------------
| id |
| scan_id |
| found_user |
-----------------
id is an autoincrementing primary key. Has no real value other than that.
scan_id is an integer that is incremented after each successful scan of all users. It so I can separate results from different scans.
found_user. Stores which user was found online during the scan.
The above will generate a table that could look like this:
id | scan_id | found_user
----------------------------
1 | 1 | Nick
2 | 2 | Nick
3 | 2 | John
4 | 3 | John
So on the first scan the system found only Nick online. On the 2nd it found both Nick and John. On the 3rd only John was still online.
My problem is that I want to get the total amount of unique users connected to the server at the time of each scan. In other words, I want the aggregate number of users that have connected at each scan. Think counter.
From the example above, the result I want from the sql is:
1
2
2
EDIT:
This is what I have tried so far, but it's wrong:
SELECT COUNT(DISTINCT(found_user)) FROM ab_logs WHERE DATE(timestamp) = CURDATE() GROUP BY scan_id
What I tried returns this:
1
2
1
The code below should give you the results you are looking for
select s.scan_id, count(*) from
(select distinct
t.scan_id
,t1.found_user
from
tblScans t
inner join tblScans t1 on t.scan_id >= t1.scan_id) s
group by
s.scan_id;
Here is sqlFiddle
It assumes the names are unique and includes current and every previous scans in the count
Try with group by clause:
SELECT scan_id, count(*)
FROM mytable
GROUP BY scan_id
I'm creating a query with Microsoft Access 2003 and had encounter an issue. I'm new!
I've got 2 tables. First table, i have a list of records that include the name, property name and the country state. Second table, i have a list of property names, the number of units in the property and the property's country state.
I will like to count the number of records in the first table by its state, meanwhile summing up the number of units the property has in the state.
What I encountered is, when I sum the number of units, the units repeats!
Taking for example;
Table1:
Name | State | Property Name
Mr 1 | State A | Building AAA
Mr 2 | State A | Building AAA
Mr 3 | State A | Building BBB
Mr 4 | State B | Building XXX
Mr 5 | State B | Building XXX
Table2:
Property Name | State | Number of Units
Building AAA | State A | 100
Building BBB | State A | 50
Building XXX | State B | 20
My Result:
State | Number of Units | No of Records
State A | 250 | 3
State B | 40 | 2
The result i want:
State | Number of Units | No of Records
State A | 150 | 3
State B | 20 | 2
EXPANDED
Assuming you are using the Access query builder, you will need to construct three Select queries:
1) Table1 will be the source table for the first query. Use the State field twice in the query, first as a Group By field and second as a Count field. (Any of the fields could have been used for the count, since you are only interested in the number of records.) Save the query for use in the third query.
2) Table2 will be the source table for the second query. Use the State field as a Group By field and the Units field as a Sum field. Save this query, too.
3) The third query will bring the information together. For the source, use the first and second queries, with a join between them on the State field. Select the State field (from either query) as a Group By Field, the CountOfState field from the first query as a Sum field, and the SumofUnits field from the second query as a Sum field.
While the actual amount of work done by Access in producing the final result will not change, the three queries can be consolidated into a single query by editing the underlying SQL.
The new query was produced by inserting the Table1 and Table2 queries into the third, final result query, one on either side of the INNER JOIN statement. The T1 and T1 in the new query are aliases for the embedded queries that eliminate ambiguity in referencing the fields of those queries.
The new query cannot be created using the Query Builder (although the original three queries provide the raw material for it). Instead, the SQL must be written/pasted in/edited in the SQL View of the Query Builder.
SELECT T1.State AS State,
Sum(T1.CountOfState) AS Records,
Sum(T2.SumOfUnits) AS Units
FROM
(SELECT Table1.State,
Count(Table1.State) AS CountOfState
FROM Table1
GROUP BY Table1.State) T1
INNER JOIN
(SELECT Table2.State,
Sum(Table2.Units) AS SumOfUnits
FROM Table2
GROUP BY Table2.State) T2
ON T1.State = T2.State
GROUP BY T1.State;