How to UPDATE a field from a SELECT table using JOIN - mysql

I have three tables.
inspectionreport
+----------+-----------------+------+
| ReportID | InspectionID_id | Date |
+----------+-----------------+------+
inspectionreport has ReportID PK, InspectionID_id FK, and date field Date.
inspection
+--------------+------------+
| InspectionID | PartID_id |
+--------------+------------+
inspection has InspectionID PK and PartID_id FK.
part
+--------+---------------+
| partID | LastInspected |
+--------+---------------+
part has a partID PK and date field LastInspected which is null by default.
What I want to do is fill LastInspected with THE LATEST Date for the relevant part.
So far I've tried using this SELECT query:
SELECT *
FROM moorings_inspectionreport
JOIN moorings_inspection
ON moorings_inspectionreport.InspectionID_id = moorings_inspection.InspectionID;
I figured I could use this to check if PartID_id matched partID as it's now only two tables. But I'm so confused as to how to pull this off as I'm quite unfamiliar with sql queries.
I also want to put this update into a daily running event that updates for every entry in the part table. So for me that adds a whole other level of confusion.

By joining the 2 tables inspection and inspectionreport and grouping by partid you can get the last lastinspected for each partid.
Then join to part and update:
update part p
inner join (
select i.partid, max(r.date) date
from inspection i
inner join inspectionreport r on r.inspectionid = i.inspectionid
group by i.partid
) t on p.partid = t.partid
set p.lastinspected = t.date

Related

Update table with data from multiple tables

I'm stuck on this one. I have three tables:
Table 1:
**ORDERS**
| ORDER_NO | PRODUCT_NO | CLIENT_NO | UNITS | ORDER_DATE |
Table 2:
**CLIENTS**
| CLIENT_NO | NAME | LOCATION | SELLER_NO | OWES | OVERPAID | CREDIT_LIMIT |
Table 3:
**PRODUCTS**
| PRODUCT_NO | DESCRIPTION | UNIT_PRICE | AVAILABLE_STOCK |
Now, what I have to do is to update column OWES in table CLIENTS so it contains total amount of money of all the orders they made.
This is as far as I got:
update CLIENTS set OWES = (select sum(o.UNITS) from ORDERS o where CLIENTS.CLIENT_NO = o.CLIENT_NO);
That seems to work just fine to get a total number of orders, but than I have to multiply it by the price of given item (whichever the order was for) and I get myself confused.
I tried for example:
update CLIENTS set OWES = ( select sum(o.UNITS) from ORDERS o where CLIENTS.CLIENT_NO = o.CLIENT_NO)*(select UNIT_PRICE from PRODUCTS where PRODUCT_NO= any(select PRODUCT_NO from ORDERS));
But that returns ERROR 1242 (21000): Subquery returns more than 1 row
What am I doing wrong? Would it be better to use update CLIENTS as ( some complicated sub query goes here) ?
Can anyone help me out and be so kind to throw in some explanation why such solution and not some other? It just seem like I didn't get something on more basic level and now I'm struggling.
Thanks in advance.
I think you can just do a join in the subquery and do the appropriate aggregation:
update CLIENTS
set OWES = (select sum(o.UNITS * p.unit_price)
from ORDERS o join
products p
on o.product_no = p.product_no
where CLIENTS.CLIENT_NO = o.CLIENT_NO
);
Your syntax is a little off. The general syntax for updating from other tables is:
UPDATE table1
JOIN table2 ON table2.mycolumn = table1.mycolumn
JOIN (
SELECT foo, SUM(bar) as sumbar FROM table3) table3sum ON table3sum.foo = table1.foo
SET
table1.foo = table2.bar,
table1.baz = table3sum.sumbar

select statement with only rows which have set true in second table

i have two tables
activity
id | user_id | time | activity_id
1 | 1 | | 3
2 | 1 | | 1
and preferences
user_id | running | cycling | driving
1 | TRUE | FALSE | FALSE
i need result set of
id | user_id | time |
2 | 1 | |
i only need rows from first table whose values are set true in preferences table.
e.g activity_id for running is 1 which is set true in preferences table, so it returns while others doesn't.
If you can edit the schema, it would be better like this:
activity
id | name
1 | running
2 | cycling
3 | driving
user_activity
id | user_id | time | activity_id
1 | 1 | | 3
2 | 1 | | 1
preferences
user_id | activity_id
1 | 1
A row in preferences indicates a TRUE value from your schema. No row indicates a FALSE.
Then your query would simply be:
SELECT ua.id, ua.user_id, ua.time
FROM user_activity ua
JOIN preferences p ON ua.user_id = p.user_id
AND ua.activity_id = p.activity_id
If you want to see the activity name in the results:
SELECT ua.id, ua.user_id, ua.time, activity.name
FROM user_activity ua
JOIN preferences p ON ua.user_id = p.user_id
AND ua.activity_id = p.activity_id
JOIN activity ON ua.activity_id = activity.id
What I would probably do is join the tables on a common column, looks like user_id is a common column in this case, which gives access to the columns in both tables to query against in the where clause of the query.
Which type of join depends on what information you want from preferences
Handy Visual Guide for joins
So you could query
SELECT * FROM activity LEFT JOIN preferences ON activity.user_id = preferences.user_id WHERE preferences.columnIWantToBeTrue = true
I'm using left join since you mentioned you want the values from the first table based on the second table.
Mike B has the right answer. The relational model relates rows together by common values.
You've got a table named activity with an id column which looks like the primary key. The column name activity_id would typically be the name of a column in another table that is a foreign key to the activity table, referencing activity.id.
It looks like you've used the activity_id column in the activity table as a reference to either "running", "cycling" or "driving".
It's possible to match activity.activity_id = 1 with "running", but this is a bizarre design.
Here's an example query:
SELECT a.id
, a.user_id
, a.time
FROM activity a
JOIN preferences p
ON p.user_id = a.user_id
AND ( ( p.running = 'TRUE' AND a.activity_id = 1 )
OR ( p.cycling = 'TRUE' AND a.activity_id = 2 )
OR ( p.driving = 'TRUE' AND a.activity_id = 3 )
)
But, again, this is a bizarre design.
As a start, each table in your database should have rows that represent either an entity (a person, place, thing, concept or event that can be uniquely identified, is important, and we need to store information about), or a relationship between the entities.
From the limited information we have about your use case, the entities appear to be "user", an "activity_type" (running, cycling, driving), an "activity" (an amount of time, for a user and an activity_type) and some user "preference" about which activity_types the user prefers.
See the answer from Mark B for a possible schema design.

Should I redesign my tables or can I make this work?

Right now I'm working on expanding my website to new functionality. I want to enable notifications from different sources. Similar to groups and people on facebook. Here is my table layout right now.
course_updates
id | CRN (id of course) | update_id
------------------------------------
courses
id | course_name | course_subject | course_number
-------------------------------------------------
users
id | name | facebook_name
---------------------------------------------------
user_updates
id | user_id | update_id
------------------------
updates
id | timestamp | updateObj
---------------------------
What I would like to be able to do is take course_update and user_updates in one query and join them with updates along with the correct information for the tables. So for course_updates i would want course_name, course_subject, etc. and for user_updates i would want the username and facebook name. This honestly probably belongs in two separate queries, but I would like to arrange everything by the timestamp of the updates table, and I feel like sorting everything in php would be inefficient. What is the best way to do this? I would need a way to distinguish between notification types if i were to use something like a union because user_updates and course_updates can store a reference to the same column in updates. Any ideas?
You might not need updates table at all. You can include timestamp columns to course_updates and user_updates tables
CREATE TABLE course_updates
(
`id` int,
`CRN` int,
`timestamp` datetime -- or timestamp type
);
CREATE TABLE user_updates
(
`id` int,
`user_id` int,
`timestamp` datetime -- or timestamp type
);
Now to get an ordered and column-wise unified resultset of all updates you might find it convenient to pack update details for each update type in a delimited string (using CONCAT_WS()) in one column (let's call it details), inject a column to distinguish an update type (lets call it obj_type) and use UNION ALL
SELECT 'C' obj_type, u.id, u.timestamp,
CONCAT_WS('|',
c.id,
c.course_name,
c.course_subject,
c.course_number) details
FROM course_updates u JOIN courses c
ON u.CRN = c.id
UNION ALL
SELECT 'U' obj_type, u.id, u.timestamp,
CONCAT_WS('|',
s.id,
s.name,
s.facebook_name) details
FROM user_updates u JOIN users s
ON u.user_id = u.id
ORDER BY timestamp DESC
Sample output:
| OBJ_TYPE | ID | TIMESTAMP | DETAILS |
-------------------------------------------------------------------------
| C | 3 | July, 30 2013 22:00:00+0000 | 3|Course3|Subject3|1414 |
| U | 2 | July, 11 2013 14:00:00+0000 | 1|Name1|FB Name1 |
| U | 2 | July, 11 2013 14:00:00+0000 | 3|Name3|FB Name3 |
...
Here is SQLFiddle demo
You can then easily explode details values while you iterate over the resultset in php.
I don't think you should mix both of those concepts (user and course) together in a query. They have different number of columns and relate to different concepts.
I think you really should use two queries. One for users and one for courses.
SELECT courses.course_name, courses.course_subject, courses.course_number,
updates.updateObj,updates.timestamp
FROM courses, updates, course_updates
WHERE courses.id = course_updates.course_id
AND course_updates.udpate_id = updates.id
ORDER BY updates.timestamp;
SELECT users.name,users.facebook_name,updates.updateObj,updates.timestamp
FROM users ,updates, user_updates
WHERE users.id = user_updates.user_id
AND user_updates.update_id = updates.id
ORDER BY updates.timestamp;
If you are going to merge the two table you need to keep in mind 2 things:
Number of columns should ideally be the same
There should be a way to distinguish the source of the data.
Here is one way you could do this:
SELECT * FROM
(SELECT courses.course_name as name, courses.course_subject as details,
updates.updateObj as updateObj, updates.timestamp as timestamp,
"course" as type
FROM courses, updates, course_updates
WHERE courses.id = course_updates.course_id
AND course_updates.udpate_id = updates.id)
UNION ALL
SELECT users.name as name,users.facebook_name as details,
updates.updateObj as updateObj,updates.timestamp as timestamp,
"user" as type
FROM users ,updates, user_updates
WHERE users.id = user_updates.user_id
AND user_updates.update_id = updates.id) as out_table
ORDER BY out_table.timestamp DESC
The type will let you distinguish between user and course updates and could be used by your front end to differently colour the rows. The course_id does not appear in this but you can add it, just keep in mind that you will have to add some dummy text to the user select statement to ensure both queries return the same number of rows. Note that in case there is an update referring to both user and course, it will appear twice.
You could also order by type to differentiate user and course data.

what should be the query for creting logical view in Mysql?

i have to write one query in mysql .following is my table of ad_display
ad_displaysId | adv_id | campaign_id | adtypeId | clicked(boolean)
i have another table name as advertise which have ad_id
ad_id | advertisename
cammpaignId is in this table name as camp_table
campaign_id | camp_name
adtypeId is in this table name as adtype
adtypeId | adtype_name
now what i want is to create one view which gives me result like this from my ad_display Table
advertise_name | camp_name | adtype_name | count_of_clicked | count_of_row
here
count_of_clicked = count of row where adv_id=? campaignId=? adtypeId=? clicked=true
count_of_row= count of row where adv_id=? campaignId=? adtypeId=?
the additional inforamtion i can give is...that in ad_display there is multiple entry with same adv_id same camp_id same ad_typeId with clicked collumn is true or false.
i am totaly new in mysql..so can anyone suggest me query to do this with mysql?
Try this:
create view my_ad_view as
select
advertise_name,
camp_name,
adtype_name,
sum(clicked) as count_of_clicked,
count(*) as count_of_row
from ad_display ad
left join advertise a on a.ad_id = ad.ad_displaysId
left join camp_table ct on ct.campaign_id = ad.campaign_id
left join adtype at on at.adtypeId = ad.adtypeId
group by advertise_name, camp_name, adtype_name
Note the nice "trick" of sum(clicked), which works because in mysql true is 1 and false is 0, so summing a boolean counts how many times it is true.

How to filter duplicates within row using Distinct/group by with JOINS

For simplicity, I will give a quick example of what i am trying to achieve:
Table 1 - Members
ID | Name
--------------------
1 | John
2 | Mike
3 | Sam
Table 1 - Member_Selections
ID | planID
--------------------
1 | 1
1 | 2
1 | 1
2 | 2
2 | 3
3 | 2
3 | 1
Table 3 - Selection_Details
planID | Cost
--------------------
1 | 5
2 | 10
3 | 12
When i run my query, I want to return the sum of the all member selections grouped by member. The issue I face however (e.g. table 2 data) is that some members may have duplicate information within the system by mistake. While we do our best to filter this data up front, sometimes it slips through the cracks so when I make the necessary calls to the system to pull information, I also want to filter this data.
the results SHOULD show:
Results Table
ID | Name | Total_Cost
-----------------------------
1 | John | 15
2 | Mike | 22
3 | Sam | 15
but instead have John as $20 because he has plan ID #1 inserted twice by mistake.
My query is currently:
SELECT
sq.ID, sq.name, SUM(sq.premium) AS total_cost
FROM
(
SELECT
m.id, m.name, g.premium
FROM members m
INNER JOIN member_selections s USING(ID)
INNER JOIN selection_details g USING(planid)
) sq group by sq.agent
Adding DISTINCT s.planID filters the results incorrectly as it will only show a single PlanID 1 sold (even though members 1 and 3 bought it).
Any help is appreciated.
EDIT
There is also another table I forgot to mention which is the agent table (the agent who sold the plans to members).
the final group by statement groups ALL items sold by the agent ID (which turns the final results into a single row).
Perhaps the simplest solution is to put a unique composite key on the member_selections table:
alter table member_selections add unique key ms_key (ID, planID);
which would prevent any records from being added where the unique combo of ID/planID already exist elsewhere in the table. That'd allow only a single (1,1)
comment followup:
just saw your comment about the 'alter ignore...'. That's work fine, but you'd still be left with the bad duplicates in the table. I'd suggest doing the unique key, then manually cleaning up the table. The query I put in the comments should find all the duplicates for you, which you can then weed out by hand. once the table's clean, there'll be no need for the duplicate-handling version of the query.
Use UNIQUE keys to prevent accidental duplicate entries. This will eliminate the problem at the source, instead of when it starts to show symptoms. It also makes later queries easier, because you can count on having a consistent database.
What about:
SELECT
sq.ID, sq.name, SUM(sq.premium) AS total_cost
FROM
(
SELECT
m.id, m.name, g.premium
FROM members m
INNER JOIN
(select distinct ID, PlanID from member_selections) s
USING(ID)
INNER JOIN selection_details g USING(planid)
) sq group by sq.agent
By the way, is there a reason you don't have a primary key on member_selections that will prevent these duplicates from happening in the first place?
You can add a group by clause into the inner query, which groups by all three columns, basically returning only unique rows. (I also changed 'premium' to 'cost' to match your example tables, and dropped the agent part)
SELECT
sq.ID,
sq.name,
SUM(sq.Cost) AS total_cost
FROM
(
SELECT
m.id,
m.name,
g.Cost
FROM
members m
INNER JOIN member_selections s USING(ID)
INNER JOIN selection_details g USING(planid)
GROUP BY
m.ID,
m.NAME,
g.Cost
) sq
group by
sq.ID,
sq.NAME