Ms Access query not working. Please suggest - ms-access

Help needed for the below query. Thanks
SELECT b.SchemeCode_Db,
'DBELE',
1,
SecurityCode_Db,
"DIRECT",
Qty,
Price,
((Commission + TransferCharge) / Qty) AS Charges,
"",
iif(Buy_sell_code = "1110",
((Qty * Price) + Commission + TransferCharge),
iif(Buy_sell_code = "1120", ((Qty * Price) - Commission
- TransferCharge
))) AS totalCost,
BrokerCode_Db,
"",
Deal,
Format(Tradedate, "dd/MM/yyyy"),
Format(Valuedate, "dd/MM/yyyy"),
'', 'BSE', 'CH', 'D', '',
iif(Buy_sell_code = "1110",
'PUR',
iif(Buy_sell_code = "1120", 'SAL')) AS txn,
'USD' AS cur
FROM tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON FundCode = SchemeCode_Client
LEFT JOIN tbl_EQUITYMapping c
ON Ticker = SecurityCode_Client
LEFT JOIN tbl_EQUITYMapping d
ON Broker = Brokercode_Client

The Access db engine requires parentheses in the FROM clause when your query includes more than one JOIN. I suspect this FROM clause version will be a step closer to something the db engine will accept.
FROM
((tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON FundCode = SchemeCode_Client)
LEFT JOIN tbl_EQUITYMapping c
ON Ticker = SecurityCode_Client)
LEFT JOIN tbl_EQUITYMapping d
ON Broker = Brokercode_Client
However, I'm uncertain whether the db engine will be confused sorting out which join field comes from which table source. I would prefix those field names with the proper table alias.
But I think your most direct route to joy for this may be to start with a new query in Design View in the query designer. Add tbl_EQUITYINPUT and 3 copies of tbl_EQUITYMapping and assign the aliases. Then set up your joins between them while still in Design View. The query designer understands the join rules which keep the engine happy, so will guide you to the correct join syntax. And it will also include the aliases with the field names in your joins.

I'm wondering if it's confused over which instance of tbl_EQUITYMapping to join against.
I'd expect to see your from block look a little more like this:
select *
FROM tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON a.FundCode = b.SchemeCode_Client
LEFT JOIN tbl_EQUITYMapping c
ON a.Ticker = c.SecurityCode_Client
LEFT JOIN tbl_EQUITYMapping d
ON a.Broker = d.Brokercode_Client
In this case, you are joining three different copies of tbl_EQUITYMapping with tbl_EQUITYINPUT. But I don't think that is what you want to do. If you are pulling data from tbl_EQUITYMapping, the select won't know which copy to pull it from.
I expect that SchemeCode_Client, SecurityCode_Client, and Brokercode_Client form a composite key for tbl_EQUITYMapping. In which case I would expect a from block that looked like this.
select *
FROM tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON (a.FundCode = b.SchemeCode_Client
AND a.Ticker = b.SecurityCode_Client
AND a.Broker = B.Brokercode_Client)
In this case, you'd get only one copy of tbl_EQUITYMapping
You could equally well do something like this:
select *
from tbl_EQUITYINPUT a,
tbl_EQUITYMapping b
where
(a.FundCode = b.SchemeCode_Client
AND a.Ticker = b.SecurityCode_Client
AND a.Broker = B.Brokercode_Client)
(the designer may or may not optimise that for you in the former version)

Related

Select from SQL database information with newest revisions

I coding web app for my client and have issue with selecting from database raports with newest revisions.
SELECT
raports.*,
r1.*,
users.*,
(SELECT COUNT(*) FROM changes WHERE changes.changes_raports_id = raports.raports_id) as changes,
(SELECT changes.changes_date FROM changes WHERE changes.changes_raports_id = raports.raports_id ORDER BY changes.changes_date DESC LIMIT 1) as last_change,
(SUM(injuries.injuries_min_procent) / COUNT(injuries_to_raports.injuries_to_raports_id)) as min,
(SUM(injuries.injuries_max_procent) / COUNT(injuries_to_raports.injuries_to_raports_id)) as max
FROM raports
LEFT JOIN users
ON users.users_id = raports.raports_users_id
LEFT JOIN changes
ON changes.changes_raports_id = raports.raports_id
LEFT JOIN raports_to_changes r1
ON r1.raports_to_changes_raports_id = raports.raports_id
LEFT JOIN injuries_to_raports
ON injuries_to_raports.injuries_to_raports_raports_id = r1.raports_to_changes_raports_id
LEFT JOIN injuries
ON injuries_to_raports.injuries_to_raports_injuries_id = injuries.injuries_id
WHERE r1.raports_to_changes_changes_id = (SELECT max(raports_to_changes_changes_id) FROM raports_to_changes r2 WHERE r2.raports_to_changes_raports_id = r1.raports_to_changes_raports_id)
GROUP BY raports.raports_id ORDER BY raports.raports_id ASC;
In columns max and min i have not correct average from injuries. When i checked it and count all injuries i had 36 when true number is 2 but i have 18 revisions. So is logic that i have looped COUNT with all revisions but i want only the newest
I try changing WHERE statements and more LEFT JOINs but nothing helped.
Could someone fixed that code?
Thank you in advanced
Based on the clues revealed by your queries, the data model may look like this:
The select list shows that you need:
users information of a reports_id
aggregated injuries_min_procent and injuries_max_procent at raports_id level. (see cte_raport_injuries)
number of changes of a raports_id (see cte_raport_changes)
the last change_date of a raports_id (see cte_raport_changes)
I'm not sure about the need for raports_of_changes based on information revealed in the question, so I'm going to ignore it for now.
with cte_raport_injuries as (
select r.raports_id,
sum(i.injuries_min_procent) / count(*) as injuries_min_procent,
sum(i.injuries_max_procent) / count(*) as injuries_max_procent
from raports r
join injuries_to_raports ir
on r.raports_id = ir.injuries_to_raports_raports_id
join injuries i
on ir.injuries_to_raports_injuries_id = i.injuries_id
group by r.raports_id),
cte_raport_changes as (
select r.raports_id,
count(c.changes_id) as changes,
max(c.changes_date) as last_change
from raports r
join changes c
on r.raports_id = c.changes_raports_id
group by r.raports_id)
select u.users_id,
r.raports_id,
ri.injuries_min_procent,
ri.injuries_max_procent,
rc.changes,
rc.last_change
from raports r
join users u
on r.raports_users_id = u.users_id
join cte_raport_injuries ri
on r.raports_id = ri.raports_id
join cte_raport_changes rc
on r.raports_id = rc.raports_id;
The result looks like this:
users_id|raports_id|injuries_min_procent|injuries_max_procent|changes|last_change|
--------+----------+--------------------+--------------------+-------+-----------+
1| 11| 15.0000| 25.0000| 2| 2022-12-02|
So my question for you is what's in reports_to_changes that you need and what's its relationship between others? For further involvement from the community, you may want to share the following information in text format:
DDLs of each tables (primary key, foreign key, column names & data types)
Some representable sample data and basic business rules
Expected output

Issue With Join Doubling Results

I have seen several posts about this on Stack Overflow, but none of them seems to give me an answer that I can understand.
I am trying to join several relations together in order to get all of the relevant information to output all routes that start in China and end in the United States.
In the SeaRoute relation, the start_port and end_port are stored as INT and in the Port relation the pid corresponds to the start_port and end_port and includes a pcountry column.
I am starting off with just trying to output everything that has a start_port that is in China. I am expecting 3 results from my Record relation as those are the only ones that start with China in the table; However, I am receiving 6 records at the output (all of the results appear to have been doubled if I go back and audit what's in the table).
While I want the right answer, I am more concerned that I have a fundamental misunderstanding of Inner Join and the other Join methods. What am I doing wrong?
SELECT *
FROM Record
INNER JOIN Goods AS Go_data
ON Record.gid = Go_data.gid
LEFT JOIN SeaRoute AS SR
ON Record.rid = SR.rid
RIGHT JOIN (SELECT pid, pcountry AS starting_port_country
FROM Port
INNER JOIN SeaRoute AS SR ON Port.pid = SR.start_port
WHERE Port.pcountry = 'China')
AS start_port_table ON SR.start_port = start_port_table.pid
From the looks of your query, you want to be INNER JOINing between the records that you have only on the routes that you want.
You know all of the SeaRoutes that start in China and end in the United States already, you do however need to join to the Ports table twice like so:
SELECT sr.rid,
sp.pcountry AS starting_port_country,
ep.pcountry AS end_port_country
FROM dbo.SeaRoute sr
INNER JOIN dbo.Port sp ON sp.pid = sr.start_port
INNER JOIN dbo.Port ep ON ep.pid = sr.end_port
WHERE sp.pcountry = 'China'
AND ep.pcountry = 'United States'
Then you just need to join that to your main query:
SELECT *
FROM Record
INNER JOIN dbo.Goods AS Go_data ON Record.gid = Go_data.gid
INNER JOIN
(
SELECT sr.rid,
sp.pcountry AS starting_port_country,
ep.pcountry AS end_port_country
FROM dbo.SeaRoute sr
INNER JOIN dbo.Port sp ON sp.pid = sr.start_port
INNER JOIN dbo.Port ep ON ep.pid = sr.end_port
WHERE sp.pcountry = 'China'
AND ep.pcountry = 'United States'
) ports ON ports.rid = Record.rid
There's no way I can explain joins to you any clearer than this page can:
https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins

Complex SQL query I can't figure out

I'm working on creating a report for the open source tool Validation Manager. I managed to pull out a report of all requirements covered after lots of work. I'm trying to make a report to list the ones not covered, but using not in gets the query into a never ending processing state.
Here's the DD diagram:
The involved tables are in the bottom right side of the diagram. Sample data can be obtained here. What would be the correct way to get uncovered requirements?
FYI: Uncovered requirements are those that doesn't have a related step and/or their children requirements are covered.
The main issue is that there are, in theory, infinite number of levels of requirement relationships and the SQL I have only works for 2 levels. Trying to figure out a way to look as deep as necessary.
As reference, see the query for the covered requirements below (which is the opposite of what I need):
select
c.covered, t.total
from
(SELECT DISTINCT
count(distinct unique_id) as covered
FROM
requirement
WHERE
requirement.id IN (select
requirement.id
from
`requirement` requirement
INNER JOIN `step_has_requirement` step_has_requirement ON requirement.`id` = step_has_requirement.`requirement_id`
INNER JOIN `step` step ON step_has_requirement.`step_id` = step.`id`
AND step.`test_case_id` = step_has_requirement.`step_test_case_id`
AND step.`test_case_test_id` = step_has_requirement.`step_test_case_test_id`
INNER JOIN `test_case` test_case ON step.`test_case_id` = test_case.`id`
AND test_case.`test_id` = step.`test_case_test_id`
INNER JOIN `test` test ON test_case.`test_id` = test.`id`
INNER JOIN `test_plan_has_test` test_plan_has_test ON test.`id` = test_plan_has_test.`test_id`
INNER JOIN `test_plan` test_plan ON test_plan_has_test.`test_plan_id` = test_plan.`id`
AND test_plan.`test_project_id` = test_plan_has_test.`test_plan_test_project_id`
INNER JOIN `test_project` test_project ON test_plan.`test_project_id` = test_project.`id`
INNER JOIN `requirement_status` requirement_status ON requirement.`requirement_status_id` = requirement_status.`id`
INNER JOIN `requirement_spec_node` requirement_spec_node ON requirement.`requirement_spec_node_id` = requirement_spec_node.`id`
AND requirement_spec_node.`requirement_spec_project_id` = requirement.`requirement_spec_node_requirement_spec_project_id`
AND requirement_spec_node.`requirement_spec_spec_level_id` = requirement.`requirement_spec_node_requirement_spec_spec_level_id`
AND requirement_spec_node.`requirement_spec_id` = requirement.`requirement_spec_node_requirement_spec_id`
INNER JOIN `requirement_spec` requirement_spec ON requirement_spec_node.`requirement_spec_id` = requirement_spec.`id`
AND requirement_spec.`project_id` = requirement_spec_node.`requirement_spec_project_id`
AND requirement_spec.`spec_level_id` = requirement_spec_node.`requirement_spec_spec_level_id`
INNER JOIN `project` project ON requirement_spec.`project_id` = project.`id`
WHERE
requirement_status.status = 'general.approved'
and (project.id = $P{target_project_id}
or project.parent_project_id = $P{target_project_id}))
or requirement.id IN (select parent_requirement_id from requirement_has_requirement where parent_requirement_id = requirement.id
and requirement_id in (select
requirement.id
from
`requirement` requirement
INNER JOIN `step_has_requirement` step_has_requirement ON requirement.`id` = step_has_requirement.`requirement_id`
INNER JOIN `step` step ON step_has_requirement.`step_id` = step.`id`
AND step.`test_case_id` = step_has_requirement.`step_test_case_id`
AND step.`test_case_test_id` = step_has_requirement.`step_test_case_test_id`
INNER JOIN `test_case` test_case ON step.`test_case_id` = test_case.`id`
AND test_case.`test_id` = step.`test_case_test_id`
INNER JOIN `test` test ON test_case.`test_id` = test.`id`
INNER JOIN `test_plan_has_test` test_plan_has_test ON test.`id` = test_plan_has_test.`test_id`
INNER JOIN `test_plan` test_plan ON test_plan_has_test.`test_plan_id` = test_plan.`id`
AND test_plan.`test_project_id` = test_plan_has_test.`test_plan_test_project_id`
INNER JOIN `test_project` test_project ON test_plan.`test_project_id` = test_project.`id`
INNER JOIN `requirement_status` requirement_status ON requirement.`requirement_status_id` = requirement_status.`id`
INNER JOIN `requirement_spec_node` requirement_spec_node ON requirement.`requirement_spec_node_id` = requirement_spec_node.`id`
AND requirement_spec_node.`requirement_spec_project_id` = requirement.`requirement_spec_node_requirement_spec_project_id`
AND requirement_spec_node.`requirement_spec_spec_level_id` = requirement.`requirement_spec_node_requirement_spec_spec_level_id`
AND requirement_spec_node.`requirement_spec_id` = requirement.`requirement_spec_node_requirement_spec_id`
INNER JOIN `requirement_spec` requirement_spec ON requirement_spec_node.`requirement_spec_id` = requirement_spec.`id`
AND requirement_spec.`project_id` = requirement_spec_node.`requirement_spec_project_id`
AND requirement_spec.`spec_level_id` = requirement_spec_node.`requirement_spec_spec_level_id`
INNER JOIN `project` project ON requirement_spec.`project_id` = project.`id`
WHERE
requirement_status.status = 'general.approved'
and (project.id = $P{target_project_id}
or project.parent_project_id = $P{target_project_id})))
order by unique_id) c,
(SELECT
count(distinct unique_id) AS total
FROM
`requirement_status` requirement_status
INNER JOIN `requirement` requirement ON requirement_status.`id` = requirement.`requirement_status_id`
INNER JOIN `requirement_spec_node` requirement_spec_node ON requirement.`requirement_spec_node_id` = requirement_spec_node.`id`
AND requirement_spec_node.`requirement_spec_id` = requirement.`requirement_spec_node_requirement_spec_id`
INNER JOIN `requirement_spec` requirement_spec ON requirement_spec_node.`requirement_spec_id` = requirement_spec.`id`
AND requirement_spec.`spec_level_id` = requirement_spec_node.`requirement_spec_spec_level_id`
AND requirement_spec.`project_id` = requirement_spec_node.`requirement_spec_project_id`
INNER JOIN `project` project ON requirement_spec.`project_id` = project.`id`
INNER JOIN `spec_level` spec_level ON requirement_spec.`spec_level_id` = spec_level.`id`
WHERE
requirement_status.status = 'general.approved'
and (project.id = $P{target_project_id}
or project.parent_project_id = $P{target_project_id})) t
Note: The SQL there is an example on how I managed to do the opposite, get the covered requirements. I would like to get the ones not covered. That SQl query is working properly.
What I am looking for is same for the parts which are not covered now(in the above)!
First thing's first
Your schema is your biggest enemy. I have attached an SQLFiddle with your step table. It is a very impressive table, but impressive isn't always workable in SQL. You would do very well to refactor your tables and make them normalized. There are very few cases where it makes sense to put multiple integers into one text file with commas separating them. If the only thing you are going to be putting in that column, ever, is integers separated by columns, you are failing to make your SQL schema even First Normal Form. While it may seem easier to design your schema this way, it is only on the face that you are doing so. In reality, you are creating a mess of epic proportions for yourself - as you may now be discovering. By failing to even meet First Normal Form, you are failing to take advantage of any of SQL's inherent relational power.
Edit This is a fairly large schema to take in. My reaction to being not 1NF was the text part of step. I don't really know what it is aiming to do so it is hard to say for sure, but it put up a big red flag when I saw multiple rows with the same integer columns in a text box separated by commas. That usually is a sign that several columns have been concatenated into one column. After examining other parts of the schema, it is clear that other parts are normalized. It doesn't appear to be an integral part of your schema, so, my mistake. That being said this is still a very complex application with a large number of cross referencing tables. Having both requirement_has_requirement and step_has_requirement as a table may have benefits but it also has serious drawbacks.
I question whether you need to differentiate steps from requirements in the way you currently are. Is a requirement not just another step? I understand you need different columns, but that could be solved by having a requirement_addendum and a step_addendum table, which can be called on at need and ignored at need as well. I note that you have versioning in requirement. Do you not anticipate that you will need versions in step? Do you anticipate that some of your requirements will be versioned at different times than others? Could a version table be created to cover the three version columns in order to have just one version_id in your requirement table and related tables?
In any case, assuming you can't do anything substantial to this table and you just need to pull a query...
My Suggestion
You have not actually defined what "as deep as necessary" is, so you need to determine what "as deep as necessary" means. If you have a formulaic way of discovering this, you can accomplish this relatively easily by creating a recursive procedure like #RandomSeed suggested. If you do not have a particularly formulaic way of determining what "as deep as necessary" is, are you going to determine by a client-defined number of levels? If so you can still rely on a stored procedure.
If you want to make these determinations on a more case-by-case basis, or if you suspect these requirements might change, you can also consider hard-coding the information into a MySQL table which stores metadata for your schema. This can allow you to determine how many steps by running an SQL SELECT query on this metadata table to receive your answer. You can easily assign each step or each scenario to have a number of recursions you want to accomplish. You would be able to use this information in a Stored Procedure.
If you can alter your table at all...
The benefit to altering your table even slightly is that you can create a terminal to step_has_requirement and requirement_has_requirement. Each record would then have a "yes" (1) or a "no" (0) to determine if there was a requirement involved. You could then run a query on any record which was a 1 or a 0 at your leisure.
requirement_has_requirement
id | status | major_version | mid_version | minor_version
.. | 0 | NULL | NULL | NULL
.. | 1 | .. | .. | ..
step_has_requirement
id | status | ...
Etc.
Query now becomes as simple as toggling between status 0 and status 1 to find your records. You can query all elements of your hierarchy at the same time.
Please note that this becomes much easier and contains less repetition if step and requirement are in one table with additional columns on separate tables based on whether it was a requirement or a step. There are no doubt drawbacks to this but there are also benefits. If you have the ability to make these changes prior to Volume 1 it could be beneficial.
Conclusion
You clearly have a very sophisticated application in development. Unfortunately the sophistication is outgrowing your schema. There is no "best" way to accomplish what you are looking for without making some minor changes to your schema. If changing your schema is not currently an option then I strongly advise you to make a quick hack so as not to waste more time and make a request to adapt the schema soon, before this becomes an even larger headache for you.
It's a mess, but I took a crack at it. By breaking it up into chunks with CTEs, you can troubleshoot smaller pieces.
;WITH
cteRequirement (id) AS
( SELECT distinct requirement.id
FROM requirement
INNER JOIN step_has_requirement
ON requirement.id = step_has_requirement.requirement_id
INNER JOIN step
ON step.id = step_has_requirement.step_id
AND step.test_case_id = step_has_requirement.step_test_case_id
AND step.test_case_test_id = step_has_requirement.step_test_case_test_id
INNER JOIN test_case
ON test_case.id = step.test_case_id
AND test_case.test_id = step.test_case_test_id
INNER JOIN test
ON test.id = test_case.test_id
INNER JOIN test_plan_has_test
ON test_plan_has_test.test_id = test.id
INNER JOIN test_plan
ON test_plan.id = test_plan_has_test.test_plan_id
AND test_plan.test_project_id = test_plan_has_test.test_plan_test_project_id
INNER JOIN test_project
ON test_project.id = test_plan.test_project_id
INNER JOIN requirement_status
ON requirement_status.id = requirement.requirement_status_id
INNER JOIN requirement_spec_node
ON requirement_spec_node.id = requirement.requirement_spec_node_id
AND requirement_spec_node.requirement_spec_project_id = requirement.requirement_spec_node_requirement_spec_project_id
AND requirement_spec_node.requirement_spec_spec_level_id = requirement.requirement_spec_node_requirement_spec_spec_level_id
AND requirement_spec_node.requirement_spec_id = requirement.requirement_spec_node_requirement_spec_id
INNER JOIN requirement_spec
ON requirement_spec.id = requirement_spec_node.requirement_spec_id
AND requirement_spec.project_id = requirement_spec_node.requirement_spec_project_id
AND requirement_spec.spec_level_id = requirement_spec_node.requirement_spec_spec_level_id
INNER JOIN project
ON project.id = requirement_spec.project_id
WHERE requirement_status.[status] = 'general.approved'
and (project.id = $P{target_project_id} or project.parent_project_id = $P{target_project_id})
),
cteParent (id) AS
( SELECT DISTINCT parent_requirement_id
FROM requirement_has_requirement
INNER JOIN cteRequirement
ON cteRequirement.id = requirement_has_requirement.requirement_id
WHERE parent_requirement_id = requirement.id
),
ListOfRequirementIDs (requirement_id) AS
( SELECT id FROM cteRequirement UNION
SELECT id FROM cteParents
),
ListOfUniqueIDs (unique_id) AS
( SELECT DISTINCT unique_id
FROM requirement_status
INNER JOIN requirement
ON requirement.requirement_status_id = requirement_status.id
INNER JOIN requirement_spec_node
ON requirement_spec_node.id = requirement.requirement_spec_node_id
AND requirement_spec_node.requirement_spec_id = requirement.requirement_spec_node_requirement_spec_id
INNER JOIN requirement_spec
ON requirement_spec_node.requirement_spec_id = requirement_spec.id
AND requirement_spec.spec_level_id = requirement_spec_node.requirement_spec_spec_level_id
AND requirement_spec.project_id = requirement_spec_node.requirement_spec_project_id
INNER JOIN project
ON project.id = requirement_spec.project_id
INNER JOIN spec_level
ON spec_level.id = requirement_spec.spec_level_id
WHERE requirement_status.status = 'general.approved'
and (project.id = $P{target_project_id} or project.parent_project_id = $P{target_project_id})
)
SELECT 'Covered' AS [Type], COUNT(ListOfRequirementIDs.requirement_id) AS Cnt
FROM ListOfRequirementIDs
UNION ALL
SELECT 'Total' AS [Type], COUNT(ListOfUniqueIDs.unique_id) AS Cnt
FROM ListOfUniqueIDs

MySQL - Multirow Sum without Subquery

I currently have this working using a Sub-query, but as the DB grows this will become HUGELY inefficient. I'm wondering if there is a more efficient way to do what I need to do without sub-queries?
I need to have my final output look like so:
Question, Answer, Responses, Charts included in Response Count
Did this work?, N/A, 26, 30
Did this work?, Yes, 4, 30
This is my current query:
SELECT
bq_text,
ba_a,
bq_id,
COUNT(ba_a) AS ba_aC,
(SELECT COUNT(*) FROM board_done_sheet WHERE sd_b_id = bs.bs_id AND sd_sub = 1) AS sd_chartnumC
FROM board_done_sheet AS sh
LEFT JOIN board_done bd
ON (bd.bd_id = sh.sd_bd_id)
LEFT JOIN boardsubs bs
ON (bd.bd_b_id = bs.bs_id)
LEFT JOIN b_q_answers ba
ON (sh.sd_s_id = ba.ba_s_id)
LEFT JOIN bsquestions bq
ON (bq.bq_id = ba.ba_q_id)
LEFT JOIN multiples m
ON (ba.ba_m_id = m.m_id)
LEFT JOIN users u
ON (u.us_id = bd.bd_d_id)
LEFT JOIN profiles p
ON (p.p_u_id = bd.bd_d_id)
LEFT JOIN users rev
ON (rev.us_id = bd.bd_rev)
WHERE sd_sub = '1' AND bq_text <> 'Date' AND bq_id = 380
GROUP BY bs_id, bq_text, ba_a
That works perfectly, the problem is it has to use sub-queries which as time goes by will get less efficient.
I'm just wondering if there is a better more efficient way to do that summed field without it.
Presumably the subquery you're concerned about is the one in your toplevel SELECT.
That is easy to refactor so it won't get repeated.
Just JOIN it to the rest of the table. You'll want this sort of thing:
SELECT
bq_text, ...
COUNT(ba_a) AS ba_aC,
countup.countup AS sd_chartnumC
FROM board_done_sheet AS sh
LEFT JOIN board_done bd
ON (bd.bd_id = sh.sd_bd_id)
...
LEFT JOIN users rev
ON (rev.us_id = bd.bd_rev)
JOIN (
SELECT COUNT(*) AS countup , sd_b_id
FROM board_done_sheet
WHERE sd_sub = 1
GROUP BY sd_b_id
) AS countup ON countup.sd_b_id = bs.bs_id
WHERE sd_sub = '1'
AND bq_text <> 'Date'
AND bq_id = 380
GROUP BY bs_id, bq_text, ba_a
The countup subquery generates a summary table of counts and ids, and then joins it to the other tables.
A JOIN cascade of this complexity may become inefficient for other reasons as your table grows if you don't structure your indexes correctly.

MySQL Database design advice - using joins

I am building an AJAX like search page which allows a customer to select a number filters that will narrow down the search. For instance, a user has selected an 'iPhone 5' and has additional filters for capacity (32GB, 64GB) & colour (black, white..).
The user can only select a single radio box per category (so they could select 32GB & Black).. but they could not select (32GB & 64GB & black as two of these belong to the 'capacity' category).
I have added the schema here on sqlfiddle (please ignore the fact i've removed the primary keys they exist in the proper app they have just been removed along with some other fields/data to minimise the sqlfiddle)
http://sqlfiddle.com/#!2/964425
Can anyone suggest the best way to create the query to do the following:
Get all the prices for device_id '2939' (iPhone 5) which has the 'attributes' of '32GB' AND 'Black'
I currently have this - but this only works when selecting for a single attribute:
// search for device with '64GB' & 'Black' attributes (this currently doesn't return any rows)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19'
AND `attribute_option_id` = '47';
// search for device with '64GB' attribute only (this currently DOES return a row)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19';
Any advice on the database design would be appreciated too
Note: I was thinking to have a new column within the 'prices' table that has the matching attribute_ids serialised - would this be not good for optimisation however (e.g would it be slower than the current method)
Since attribute_option_id is an atomic value, it cannot have two different values for the same row. So your WHERE clause cannot match any record:
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19' # Here for one row, attribute_option_id is either 19
AND `attribute_option_id` = '47'; # of '47'. Cannot be the both
Instead of JOIN, you could try a subquery if you feel that is more readable. I think MySQL allow that syntax:
SELECT `prices`.*
FROM `prices`
WHERE `prices`.`device_id` = '2939'
AND EXISTS (SELECT *
FROM prices_attributes
WHERE price_id = `prices`.`id`
AND attribute_option_id IN ('19', '47') )
I don't know how MySQL will optimize the above solution. An alternative would be:
SELECT `prices`.*
FROM `prices`
WHERE `prices`.`id` IN (
SELECT DISTINCT `price_id`
FROM prices_attributes
WHERE attribute_option_id IN ('19', '47')
)
I think you should use the IN operator for the attribute_option_id and you set the values dynamically to the query; Also, using group_by you have only one row per price so in effect you get all the prices. Apart from this, the design is ok.
Here, I have made an example:
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
and `attribute_option_id` in ('19','47')
group by `prices`.`device_id`, `prices`.`price`;
Here, you can also add an order clause to order by price:
order by `prices`.`price` desc;
Another way to solve this would be to use a distinct on price, like this:
select distinct(prices.price)
from prices
where prices.device_id = 2939
and id in (select price_id from prices_attributes where attribute_option_id in (19,47));
Join against the devices_attributes_options table several times, once for each attribute the item must have
Something like this:-
SELECT *
FROM devices a
INNER JOIN prices b ON a.id = b.device_id
INNER JOIN prices_attributes c ON b.id = c.price_id
INNER JOIN devices_attributes_options d ON c.attribute_option_id = d.id AND d.attribute_value = '32GB'
INNER JOIN devices_attributes_options e ON c.attribute_option_id = e.id AND e.attribute_value = 'Black'
WHERE a.id = 2939
As to putting serialised details into a field, this is a really bad idea and would come back to bite you in the future!
SELECT * FROM prices WHERE device_id=2939 AND id IN (SELECT price_id FROM prices_attributes WHERE attribute_option_id IN (19,47));
Is it what you're looking for?
EDIT: sorry, didn't notice you're asking for query using joins