SQL Statement Inner Join + Others...So join within a join? - mysql

I have the following SQL statement where I am adding game inventory and ordered inventory and then selecting the games that have that total inventory below the reorder point. The only problem is that I also want to select the game inventory below the reorder point that does not have any ordered inventory. I tried messing with the Having line where ItemIDs don't match, but no luck. Here is the current working code.
What I have:
SELECT tblGames.ItemID, tblGames.Title, ABS(tblGames.Inventory + Sum(tblSupplyOrders.Inventory)) AS UpdatedInventory
FROM tblGames
INNER JOIN tblSupplyOrders
ON tblGames.ItemID=tblSupplyOrders.ItemID
Group By tblGames.ItemID, tblGames.Title, tblGames.Inventory, tblGames.ReorderPoint
Having ABS(tblGames.Inventory + Sum(tblSupplyOrders.Inventory)) < tblGames.ReorderPoint
What I also want to display with it:
SELECT tblGames.ItemID, tblGames.Title, tblGames.Inventory
FROM tblGames
INNER JOIN tblSupplyOrders
ON tblGames.ItemID=tblSupplyOrders.ItemID
Group By tblGames.ItemID, tblGames.Title, tblGames.Inventory, tblGames.ReorderPoint
Having tblGames.Inventory < tblGames.ReorderPoint
...where there is no tblSupplyOrders.ItemID
Thanks in advance!
Update Requested
tblGames includes:
ItemID int, auto PK
Title varchar(100)
Inventory int,
ReorderPoint int
tblSupplyOrders includes:
SupplyOrder int, auto PK
ItemID int, FK to tblGames
Inventory int
So if:
tblGames/tblSupplyOrders.ItemID = The Same #
tblGames.Inventory = 2
tblSupplyOrders.Inventory = 3
tblGames.ReorderPoint = 6
Output will include this game.
The problem: It won't detect this game if there is no matching ItemID in tblSupplyOrders.

I think you are overcomplicating things:
SELECT tg.ItemID, tg.Title, tg.Inventory
FROM tblGames tg
LEFT JOIN (
select ItemID, sum(inventory) as Inventory
from tblSupplyOrders
group by ItemID) tso
ON tg.ItemID=tso.ItemID
where tg.Inventory+coalesce(tso.Inventory,0) < tg.ReorderPoint
This will left-join tblGames to a subquery of tblSupplyOrders that aggregates all orders for a particular item. This will return all rows from tblGames and matching rows of order aggregate from tblSupplyOrders, with nulls where there is no matching tso row.
The expression tg.Inventory+coalesce(tso.Inventory,0) calculates current inventory + pending orders; the coalesce is needed to deal with cases where there is no tso row, substituting zero instead of null for pending orders.

Related

Retrieve all the values that are in the the row with the max value

I have a table that looks like this:
For each COMPANY there are multiple NATURAL_PERSON_ID, every NATURAL_PERSON have a date in which an audit was performed FECHA_DE_REPORTE and as a company there is a date in which the first loan was give to that company.
What I want is to select for each NATURAL_PERSON all the FOLIO_CONSULTA whose FECHA_DE_REPORTE is less or equal to FIRST_LOAN (the date in which the first loan was given for that company) Then I need to find the MAX date among each group and keep al the information (the whole row) for the value that fulfills all these conditions, and all this for each NATURAL_PERSON
So for this example the result I expected is all the information of the second row since this is the MAX() of FECHA_DE_REPORTE by COMPANY AND NATURAL_PERSON.
I have tried:
SELECT NPC.COMPANY_ID
,NPC.NATURAL_PERSON_ID
,NPS.DIGITAL_SIGNATURE_ID
,CDC.FOLIO_CONSULTA
,CDC.FECHA_DE_REPORTE
,FIRST_LOAN.FIRST_LOAN
,MAX(CDC.FECHA_DE_REPORTE) MAX_FOLIO_CONSUTA
FROM KONFIO.NATURAL_PERSON_COMPANY NPC
LEFT JOIN KONFIO.NATURAL_PERSON_SIGNATURE NPS ON NPS.NATURAL_PERSON_ID = NPC.NATURAL_PERSON_ID
JOIN KONFIO.CDC_RESPONSE CDC ON CDC.DIGITAL_SIGNATURE_ID= NPS.DIGITAL_SIGNATURE_ID
JOIN
(
SELECT CAPP.COMPANY_ID
,MIN(LOAN.DOCUMENTATION_DATE) FIRST_LOAN
FROM KONFIO.COMPANY_APPLICATION CAPP
JOIN KONFIO.LOAN ON LOAN.APPLICATION_ID = CAPP.APPLICATION_ID
GROUP BY CAPP.COMPANY_ID) FIRST_LOAN ON FIRST_LOAN.COMPANY_ID = NPC.COMPANY_ID
WHERE CDC.FECHA_DE_REPORTE <= FIRST_LOAN.FIRST_LOAN
AND NPC.COMPANY_ID IN (1033)
GROUP BY NPC.COMPANY_ID, NPC.NATURAL_PERSON_ID
but it retrieves the first value that finds so the FOLIO_CONSULTA does not correspond to the FOLIO_CONSULTA of the MAX() FECHA_DE_REPORTE
Any help would be appreciated
You should join the subquery for MAX(FECHA_DE_REPORTE) on table CDC_RESPONSE
SELECT NPC.COMPANY_ID
,NPC.NATURAL_PERSON_ID
,NPS.DIGITAL_SIGNATURE_ID
,CDC.FOLIO_CONSULTA
,CDC.FECHA_DE_REPORTE
,FIRST_LOAN.FIRST_LOAN
,T.MAX_FOLIO_CONSUTA
FROM KONFIO.NATURAL_PERSON_COMPANY NPC
INNER JOIN (
SELECT DIGITAL_SIGNATURE_ID
, MAX(FECHA_DE_REPORTE) MAX_FOLIO_CONSUTA
FROM KONFIO.CDC_RESPONSE
GROUP BY DIGITAL_SIGNATURE_ID
) T ON T.DIGITAL_SIGNATURE_ID = NPS.DIGITAL_SIGNATURE_ID
AND T.MAX_FOLIO_CONSUTA = CDC.FECHA_DE_REPORTE
LEFT JOIN KONFIO.NATURAL_PERSON_SIGNATURE NPS ON NPS.NATURAL_PERSON_ID = NPC.NATURAL_PERSON_ID
JOIN KONFIO.CDC_RESPONSE CDC ON CDC.DIGITAL_SIGNATURE_ID= NPS.DIGITAL_SIGNATURE_ID
JOIN
(
SELECT CAPP.COMPANY_ID
,MIN(LOAN.DOCUMENTATION_DATE) FIRST_LOAN
FROM KONFIO.COMPANY_APPLICATION CAPP
JOIN KONFIO.LOAN ON LOAN.APPLICATION_ID = CAPP.APPLICATION_ID
GROUP BY CAPP.COMPANY_ID) FIRST_LOAN ON FIRST_LOAN.COMPANY_ID = NPC.COMPANY_ID
WHERE CDC.FECHA_DE_REPORTE <= FIRST_LOAN.FIRST_LOAN
AND NPC.COMPANY_ID IN (1033)
GROUP BY NPC.COMPANY_ID, NPC.NATURAL_PERSON_ID
...... missing part

Working on three tables, my grouping by it's not working

I have to list the difference between the number of tickets sold at the different
stations of one line between time1 and time2. I have three table: time1, time 2 and place.
create table fares_jan18 (
station varchar(100),
ff int
);
create table fares_feb1 (
station varchar(100),
ff int
);
create table stations (
name varchar(100),
line varchar(50)
);
I'm using this query:
SELECT
fares_jan18.station AS name,
SUM(fares_feb1.ff - fares_jan18.ff) AS diff_feb1_jan18
FROM
fares_jan18
JOIN fares_feb1 ON fares_jan18.station = fares_feb1.station
JOIN stations ON fares_jan18.station = stations.name
WHERE
stations.line ="Broadway"
GROUP BY
name
ORDER BY
name;
it does give me the correct table, but when ever there is more that one record, I get the duplicate the sum of the difference. For instance the result for a station with two values should be 33254, and it' giving me 66508. I have removed the group by from my query, it seems that I have for records instead of two.
I've read all I could on StackOverflow but I can't get my head around what I'm doing wrong? Thanks!!
Here is an image of my table:
You're creating a cartesian product of all the rows with the same stations in the two fares tables, and then summing the differences from each pair. You need to calculate the totals in each table separately in subqueries, and then subtract them.
SELECT s.name, feb1.total - jan18.total AS diff_feb1_jan18
FROM stations AS s
JOIN (SELECT station, SUM(ff) AS total
FROM fares_feb1
GROUP BY station) AS feb1
ON feb1.station = s.station
JOIN (SELECT station, SUM(ff) AS total
FROM fares_jan18
GROUP BY station) AS jan18
ON jan18.station = s.station
WHERE s.line = "Broadway"
ORDER BY s.name
You need to pre-aggregate each separately or you are getting a Cartesian result... Since the query is for a single Broadway, a single record will result in each station when working the join from each date data source
SELECT
justF18.name,
justFeb1.SumFF - justF18.SumFF as FF_Diff
from
( SELECT f18.station AS name,
SUM(f18.ff) AS Sumff
FROM
fares_jan18 f18
JOIN stations s ON f18.station = s.name
AND s.line = "Broadway"
group by
f18.station ) justF18
JOIN
( SELECT feb1.station AS name,
SUM(feb1.ff) AS Sumff
FROM
fares_feb1 feb1
JOIN stations s ON feb1.station = s.name
AND s.line = "Broadway"
group by
feb1.station ) justFeb1
ON justF18.name = justFeb1.name
order by
JustFeb18.name

MySQL: How to retrieve all rows with a duplicate field, matching a LIKE on a certain field in any matched row

I have a database containing about 300,000 rows of product information.
I need to retrieve rows with duplicate UPCs (COUNT(upc) > 1), where at least one of the results' descriptions matches a certain string ("Reed", for example.)
For example, the following rows would all be selected (desc, upc pairs)
Deer D7394 62226173
Reed R2536 62226173
Deer D7217 62226173
but none of
Deer D0173 62278389
Deer D7289 62278389
Deer D9272 62278389
Here is the query I'm working with:
SELECT a.desc, a.upc, a.sku, a.short_description
FROM inventory a
JOIN
(SELECT upc, desc
FROM inventory
GROUP BY upc
HAVING COUNT(upc) > 1) b
ON a.upc = b.upc
WHERE ((a.desc LIKE '%Reed%') OR (b.desc LIKE '%Reed%'))
AND a.upc != ''
AND a.upc != 0
ORDER BY upc;
I am relatively new to MySQL, but this seems like it should work. However, some results are failing to return the non-matching row (i.e. Reed R2536 will be returned, but not Deer D7394).
Any insight would be greatly appreciated!
Brian's group_concat method will work, when the number of duplicates is small, but it will fail silently when it's not. You'll never know; you'll just be missing rows that should be present.
What you want to do is select all of the UPCs where at least one description matches (and for which duplicates exist), and then, from that list, select all rows matching each of those UPCs.
If you group all items by UPC, then you can annotate each one with a count, and flag whether any of the descriptions matched:
SELECT upc, COUNT(*) c, MAX(`desc` LIKE '%Reed%') desc_matches
FROM inventory
GROUP BY upc
(This takes advantage of the fact that boolean operators, like LIKE, actually return 0 for false and 1 for true. Taking the maximum of that column tells you whether any row matched)
Then you can filter that list based on your criteria, to get just the UPCs you are interested in:
SELECT upc, COUNT(*) c, MAX(`desc` LIKE '%Reed%') desc_matches
FROM inventory
GROUP BY upc
HAVING desc_matches = 1 AND c > 1
Once you have that list, you want to see all products that match any of those UPCs. You can do that with a simple (not OUTER) join:
SELECT a.desc, a.upc, a.sku, a.short_description
FROM inventory a
JOIN
( SELECT upc, COUNT(*) c, MAX(`desc` LIKE '%Reed%') desc_matches
FROM inventory
GROUP BY upc
HAVING desc_matches = 1 AND c > 1
) b USING (upc)
Another possible way to do this, assuming that you don't have TOO many duplicate records would be:
select * from inventory i
join (
SELECT upc
FROM inventory
GROUP BY upc
HAVING COUNT(upc) > 1
and group_concat(`desc`) like '%reed%') as available_upc
on available_upc.upc = i.upc
This assumes that your tables looks something like:
CREATE TABLE inventory(
sku CHAR(32) NOT NULL,
`desc` CHAR(32) NOT NULL,
upc CHAR(32) NOT NULL,
short_description CHAR(32) NOT NULL,
PRIMARY KEY (sku)
);
insert into inventory values ('D7394','Deer','62226173','Small Deer');
insert into inventory values ('R2536','Reed','62226173','Small Reed');
insert into inventory values ('D7217','Deer','62226173','Large Deer');
insert into inventory values ('D0173','Deer','62278389','Small Deer');
insert into inventory values ('D7289','Deer','62278389','Small Reed');
insert into inventory values ('D9272','Deer','62278389','Large Deer');
Hard to tell without testing, but try:
SELECT a.desc, a.upc, a.sku, a.short_description
FROM inventory a
OUTER RIGHT JOIN
(SELECT upc
FROM inventory
GROUP BY upc
HAVING COUNT(upc) > 1) b
ON a.upc = b.upc
WHERE ((a.desc LIKE '%Reed%') OR (b.desc LIKE '%Reed%'))
AND a.upc != ''
AND a.upc != 0
ORDER BY upc;
The key is the OUTER RIGHT JOIN. Please see the article: http://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins
Also, you only need to return the upc from the inner SELECT query.

multiple conditions on same column

Have seen multiple posts on this but I can't see any which answer my question.
Basically I have 3 tables in my database which relate to members and their categorisation/classification.
members (defines list of members and associated data)
member_taxonomy (defines categories, subcategories and facilities using combination of partent id and enumerated field tax_type (CATEGORY, SUBCATEGORY, FACILITY)
member_taxonomy_map (defines mapping between members and member_taxonomy)
I have a members page within which are a number of options to refine the search by specifying one or more subcategory and one or more facility. I have been trying to search on the table using the query:
SELECT members.*
FROM (members)
JOIN member_taxonomy_map ON member_taxonomy_map.member_id = members.id
WHERE member_taxonomy_map.taxonomy_id = 1
AND member_taxonomy_map.taxonomy_id = 26
AND members.active = 1
AND members.deleted = 0;
However this returns 0 rows which is something to do with having multiple where clauses on the same column but I can't figure out how this query should look. Each time the search is refined (and there could be up to 30 different options to refine the search) I need add an additional AND clause so that only members with these mappings are returned.
An IN clause will not work as this is effectively returning any rows which match any of these particular values but this is incorrect as it needs to match exactly the values specified.
Hopefully someone can give me a few pointers in the right direction.
Thanks in advance.
UPDATE
It is possible that taxonomy_id can be 1 and 26. I prob need to include the schema for the members_taxonomy_map table.
id int
tax_name varchar
slug varchar
tax_type enum ('CATEGORY','CLASSIFICATION','FACILITY','RATING')
parent_id int
active int
Therefore any tax_type with no parent id set are top level CATEGORY. Subcategories have a parent_id CATEGORY. CLASSIFICATION's can have a parent_id of the CATEGORY and FACILITY's have a parent_id of CATEGORY.
Therefore for example a category could be accommodation, sub-category could be hotel and facility could be wifi. Therefore if a member has all three of these items they will have 3 entries in the mapping table. I need to be be able to filter these so that it builds up the query to filter depending on the accommodation types (i.e subcategories - those entries with a tax_type of CATEGORY but also have a parent id, then within this the classifications. The query may return multiple entries for the same member but I can deal with this by filter these out with extra SQL clauses.
SELECT members.*
FROM (members)
JOIN member_taxonomy_map ON member_taxonomy_map.member_id = members.id
WHERE (member_taxonomy_map.taxonomy_id = 1
OR member_taxonomy_map.taxonomy_id = 26)
AND members.active = 1
AND members.deleted = 0;
It's probably not possible for a record to have a taxonomy_id of 1 and 26; you are probably trying to get a record that contains one or the other.
SELECT mb.*
FROM members mb
JOIN member_taxonomy_map tm ON tm.member_id = mb.id
WHERE tm.taxonomy_id IN ( 1, 26)
AND mb.active = 1
AND mb.deleted = 0
;
... Or ...
SELECT mb.*
FROM members mb
WHERE EXISTS ( SELECT *
FROM member_taxonomy_map tm
WHERE ON tm.member_id = mb.id
AND tm.taxonomy_id IN ( 1, 26)
)
AND mb.active = 1
AND mb.deleted = 0
;
... Or ...
SELECT mb.*
FROM members mb
WHERE mb.id IN ( SELECT tm.member_id
FROM member_taxonomy_map tm
WHERE tm.taxonomy_id IN ( 1, 26)
)
AND mb.active = 1
AND mb.deleted = 0
;
You need to JOIN member_taxonomy_map for every taxonomy_id
SELECT members.*
FROM members
JOIN member_taxonomy_map mtm1 ON mtm1.member_id = members.id AND mtm1.taxonomy_id=1
JOIN member_taxonomy_map mtm26 ON mtm26.member_id = members.id AND mtm26.taxonomy_id=26
WHERE members.active = 1
AND members.deleted = 0;

SQL Query to Calculate Values based on Conditions

I have three relevant tables:
Evaluations:
EvalID
WorktypeID
PointsPossible
Items:
EvalID
ItemID
Value
QuestionID
Question_Worktype:
WorkTypeID
QuestionID
Points
There are multiple Items for each Evaluation, and each item has a QuestionID/WorkTypeID pair that relates to an entry in Question_Worktype, where the score lies.
Items.Value is whether an Item was correct or not.
I would like a query that returns the total Points of all Items for an Evaluation, Points only are awarded if the Value of the Item is 1.
My goal would be to get:
Eval_ID, Total Points (calculated), Points Possible
I can't seem to wrap my brain around it.
Edit: I forgot an important part:
I have an additional table:
qa_edits:
EditID
ItemID
NewValue
Date
If someone edits the value, it's stored here, and I really need it to calculate based on the newest edit for the item if there is one, or the item value if there isn't.
Start with your edit table, to get the latest edit for each item:
SELECT
M.ItemId,
COALESCE(Q.Value, I.Value) as Value,
I.QuestionId
FROM (
SELECT
I.ItemId,
MAX(Date) as LastEditDate
FROM Items as I
LEFT OUTER JOIN QA_Edits as E ON
I.ItemId = E.ItemId
GROUP BY
I.ItemId
) as M
LEFT OUTER JOIN QA_Edits as Q ON
M.ItemId = Q.ItemId
AND M.LastEditDate = Q.Date
LEFT OUTER JOIN Items as I ON
M.ItemId = Q.ItemId
AND M.LastEditDate IS NULL
I'd suggest a view for that - let's call it LatestItems.
Then, back to Blorgbeard's:
select
e.Eval_ID,
sum(qw.Points) as TotalPoints,
e.PointsPossible
from Evaluations e
join LatestItems i on
e.Eval_ID = i.Eval_ID
and i.Value = 1
join Questions_WorkType qw on
qw.QuestionID = i.QuestionID
group by
e.Eval_ID, e.PointsPossible
I think this should work:
select e.Eval_ID, sum(qw.Points) as TotalPoints, e.PointsPossible
from Evaluations e
left join Items i on e.Eval_ID = i.Eval_ID and i.Value = 1
join Questions_WorkType qw on qw.QuestionID = i.QuestionID
group by e.Eval_ID, e.PointsPossible