Combine columns from different tables and assign to new table - php

I am working on a small project, where I have 2 different tables, which shows the inventory count of users A and B. Consider the table names to be the same. In each table, I have a column called count, which indicates the respective inventory count of each user. So:
A.item | A.count B.item | B.count C.item | c.count
XYZ | 25 XYZ | 31 XYZ | 0
I have a third table C, which has an empty count at the moment. As you can see, the item name (or an id) is common for all 3 tables. What I want to do, is to add the count for users A and B, and then assign it to C. So, what I think I should be doing is;
UPDATE C set count = A.count + B.count WHERE A.item = B.item
Obviously the above syntax doesn't work. The only thing I've managed to get going so far, is to just show the respective counts from both the table, by using the following code:
SELECT A.count, B.count
FROM A
INNER JOIN B ON A.item = B.id
LIMIT 0 , 30
But with the above code, I don't know how to proceed, so that I can sum the counts from A and B, then assign it to C.count. I tried using a while loop in php, and going through each count row by row - querying over and over, but it takes a long time, and it usually times out, based on the default php timeout setting.
Any help would really be appreciated.
Edit: The above question has been clearly answered by Tim. I'm wondering, how do I modify Tim's code, so that, instead of the count, now I have 2 columns with strings. So:
A.item | A.comment B.item | B.comment C.item | c.comment
XYZ | 25 XYZ | 31 XYZ | 0
How do I modify Tim's code to take the comments from A and B, and add them to C? I tried the following:
UPDATE C c
LEFT JOIN A a
ON a.item = c.item
LEFT JOIN B b
ON b.item = c.item
SET c.comment = COALESCE(a.comment, 0) + COALESCE(b.comment, 0)
But it doesn't seem to be this straight forward. I tried looking up the documentation for COALESCE, but i was not able to relate it to this issue I'mm having now, with string. Any help?

Join the three tables together by item, and then update the count of C as being the sum of the other two.
UPDATE C c
LEFT JOIN A a
ON a.item = c.item
LEFT JOIN B b
ON b.item = c.item
SET c.count = COALESCE(a.count, 0) + COALESCE(b.count, 0)
I used left join only in the above update, in case a given item in C have only a count in A but not B, or vice-versa. If you instead want the count to be assigned only if both A and B have counts, then replace the left joins with inner joins.

Actually, following on from Tim's code, this code solves my updated question - i.e. joining strings.
UPDATE C c
LEFT JOIN A a
ON a.item = c.item
LEFT JOIN B b
ON b.item = c.item
SET c.comment = CONCAT(c.comment, a.comment, b.comment);

Related

How to perform JOIN operation and to remove duplicate rows, using PHP + MySQL?

I have two tables like below:
table-a
table-b
The color code shows how the entries of table are related. table-a shows item name and it's constituent tags (let it be the ids of it's ingredients / components). Table-b shows the form in which the item is available, say, item 1111 is available in cup, item 2222 in both cone and bar etc. What I need is, when I give tagid, I want to retrieve corresponding types in which it is available.
If it give 101,102, it should return;
cup
If it give 101,103, it should return;
cone
bar
cup
In the second case, note that it actually returns
cone
bar
cone
cup
But I don't want cone to be repeated. I don't want duplicate rows.
How can I do this? Is this some kind of JOIN operation? I use PHP+MySQL. Is this possible with MySQL alone / with the help of PHP?
MySQL query example:
select distinct type
from tableb b inner join tablea a on b.product = a.item
where a.tagid IN (101,103)
SELECT tableb.type
FROM tableb INNER JOIN tablea ON tablea.item = tableb.product
WHERE tablea.tagid IN ('YOUR-IDS-ARRAY')
GROUP BY tableb.type;
select type from tableb b inner join tablea a on b.product = a.item where a.tagid IN (101,103) group by type

MySQL join multiple rows of query into one column

Table structure
client_commands (the "main" table):
id | completed
command_countries:
id | command_id | country_code
command_os:
id | command_id |OS
command_id on references the id column on client_commands.
Problem
I can add client commands with filters based on countries and operating systems. To try and normalise my DB structure, for each new command added:
Add a new row to client_commands
For each country, I add a new row to command_countries, each referencing client_command.id
For each OS, I add a new row to command_os, each referencing client_command.id
For one of the pages on my site, I need to display all client_commands (where completed = 0) as well as all the countries and operating systems for that command. My desired output would be something like:
id | countries | OS
1 | GB, US, FR| 2, 3
2 | ES, HU | 1, 3
I'm not sure how to go about doing this. The current query I'm using returns multiple rows:
SELECT a.id, b.country_code, c.OS
FROM client_commands a
LEFT JOIN command_countries b on b.command_id = a.id
LEFT JOIN command_os c on c.command_id = a.id
WHERE a.completed = 0
Any help?
Thanks!
EDIT: I forgot to mention (if you couldn't infer from above) - there can be a different number of operating systems and countries per command.
--
Also: I know I could do this by pulling all the commands, then looping through and running 2 additional queries for each result. But if I can, I'd like to do it as efficiently as possible with one query.
You can do this in one query by using GROUP_CONCAT
SELECT a.id,
GROUP_CONCAT(DISTINCT b.country_code SEPARATOR ' ,') `countries`,
GROUP_CONCAT(DISTINCT c.OS SEPARATOR ' ,') `os`,
FROM client_commands a
LEFT JOIN command_countries b on b.command_id = a.id
LEFT JOIN command_os c on c.command_id = a.id
WHERE a.completed = 0
GROUP BY a.id
if you want the ordered results in in a row you can use ORDER BY in GROUP_CONCAT like
GROUP_CONCAT(b.country_code ORDER BY b.command_id DESC SEPARATOR ' ,') `countries`
But be aware of that fact it has a limit of 1024 character to concat set by default but this can be increased b,steps provided in manual

MySQL SELECT COUNT from one table and ID from another?

I'm trying to query information from two different tables, but I'm not figuring out how to do it best. As a disclaimer, I'm still learning MySQL/PHP, and I don't have control over the tables as they're set up - I'm trying to work with what I've got, since I can't add/change the tables. Below are the tables and the relevant attributes:
Table(attribute1, attribute2, ...);
------------------------------------
reports(id, reporter_id, added)
report_comments(comment_id, report_id, comment_text, commenter_id)
The reporter_id refers to the user who filed a report, and commenter_id is not the same person as reporter_id.
I want to get a count of how many report comments have, for example, the word "incorrect" in comment_text, for each reporter_id. I then want to make a table that shows each reporter's ID and the number of comments that are associated with that reporter's reports since "1383359439" (timestamp).
So far, I've not been very successful. My current query looks like this:
SELECT r.id, r.reporter_id,
(SELECT COUNT(*) FROM report_comments WHERE comment_text LIKE '%incorrect%' AND report_id = r.id) AS comments
FROM reports AS r
LEFT JOIN report_comments AS rc ON r.id = rc.report_id
WHERE r.added > 1383359439
GROUP BY r.reporter_id;
The resulting page, when I set the HTML table to list "reporter_id" followed by "comments", gives everyone who has filed a report since the time listed, but the count is either "0" or "1", with any reporter who has had "incorrect" in any report comment getting a "1" and those without "incorrect" getting "0":
Reporter1 | 0
Reporter2 | 1
Reporter3 | 0
Reporter4 | 1
Reporter5 | 1
The thing is, some reporters have had several comments with "incorrect" in them, and I want to get a count of each, and ONLY for those reporters (not ones who've never had an "incorrect" comment). For example:
Reporter2 | 2
Reporter4 | 17
Reporter5 | 3
I'm clearly missing something - what am I doing wrong?
You need to utilize grouping for this.
SELECT
r.reporter_id AS `reporter_id`,
COUNT(rc.report_id) AS `incorrect_count`
FROM reports AS r
INNER JOIN report_comments AS rc
ON r.id = rc.report_id
WHERE rc.comment_text LIKE '%incorrect%'
AND r.added > ?
GROUP BY `reporter_id`
Here ? represents the timestamp you are trying to compare against.
To answer your follow-up question, there are a couple of ways to do this. I might suggest use of SUM() in conjunction with CASE like this:
SELECT
r.reporter_id AS `reporter_id`,
SUM(
CASE WHEN rc.comment_text LIKE '%incorrect%'
THEN 1
ELSE 0
END CASE
) AS `incorrect_count`,
SUM(
CASE WHEN rc.comment_text LIKE '%fake%'
THEN 2
ELSE 0
END CASE
) AS `fake_count`,
FROM reports AS r
INNER JOIN report_comments AS rc
ON r.id = rc.report_id
WHERE
rc.comment_text LIKE '%incorrect%'
OR rc.comment_text LIKE '%fake%'
AND r.added > ?
GROUP BY `reporter_id`
It's something like this:
SELECT r.reporter_id, COUNT(*) comments
FROM reports AS r
INNER JOIN report_comments AS rc ON r.id = rc.report_id
WHERE r.added > 1383359439
AND comment_text LIKE '%incorrect%'
GROUP BY r.reporter_id;
I removed r.id since it doesn't make sense to have in this case as one reporter can have many reports (so multiple r.id).
You could try
SELECT r.id, COUNT(c.id) tot
FROM reports r INNER JOIN report_comments
ON r.id = c.report_id
AND c.comment_text LIKE '%incorrect%'
AND r.added > 1383359439
GROUP BY r.reporter_id

Left Joining three MySQL tables

tbl_teams: team_id | team_name
tbl_players: player_id | player_fname | player_sname | player_bplace | player_bdate
tbl_players_stats: player_id | season_id | player_squad_no | team_id | player_apps | player_goals
Sorry if this is a basic question, but from all the MySQL tables and columns above I'd like to join the tables and then display the results by which season_id and team_id is selected. I need using PHP like this:
player_squad_no | player_sname, player_fname | team_name | player_apps | player_goals
I've looked at examples on here but still can't figure out how to write the MySQL query to do it with three separate tables and how to specify the table name before the column name. I've seen some examples with only the initial. tt.teams for instance. Is Left Join the way to do it?
Any help would be much appreciated.
With three separate tables, you simply write the join like this:
SELECT *
FROM Table_A AS A
LEFT JOIN Table_B AS B USING(ID)
LEFT JOIN Table_C AS C USING(ID)
Note that USING(column) is a syntactic alternative to ON A.column = B.column that you can use when the columns you want to join on have the same name in both tables.
In the above example, the tables are aliased with AS so that you can refer to them by the alias instead of the full table name. (AS is actually optional; you can just give the alias immediately after the table, if you're paying by the character.) Try to choose an alias that makes sense when you look at it; often times people will alias like this:
SELECT a.Name, b.State
FROM Customers AS a
LEFT JOIN Orders AS b
...etc.
But if you have a longer query, how are you supposed to remember what tables a and b refer to? At the very least, it would make sense to alias Customers AS C and Orders AS O; in some cases, I would go a step further: Registration AS REG, for instance. This gets more and more important as you JOIN more and more tables together.
Here's one way to write your query:
SELECT
Stats.player_squad_no,
CONCAT_WS(', ', Players.player_sname, Players.player_fname) AS player_full_name,
Teams.team_name,
Stats.player_apps,
Stats.player_goals
FROM tbl_players AS Players
LEFT JOIN tbl_players_stats AS Stats USING(player_id)
LEFT JOIN tbl_teams AS Teams USING(team_id)
The CONCAT_WS() function is included to assemble the player's full name the way you indicated you wanted it to be displayed. Since this function will output a column with a messy name, I also gave it an alias.
This should work
SELECT tbl_players_stats.player_squad_no,
tbl_players.player_sname,
tbl_players.player_fname,
tbl_teams.team_name,
tbl_players_stats.player_apps,
tbl_players_stats.player_goals
FROM tbl_players
JOIN tbl_players_stats ON tbl_players.player_id = tbl_players_stats.player_id
JOIN tbl_teams ON tbl_teams.team_id = tbl_players_stats.team_id
SELECT player_squad_no , player_sname, player_fname,team_name, player_apps, player_goals
FROM tbl_players_stats as s
JOIN tbl_players as p ON s.player_id=p.player_id
JOIN tbl_teams as t ON s.team_id=t.team_id
Nothing Joining is simple concept. But we should use proper columns for tables. While selecting the list of columns to select we should be little careful by using table aliasing. Try the below code
select c.player_squad_no,b.player_sname,b.player_fname,a.team_name,c.player_apps,c.player_goals
from tbl_teams a,tbl_players b,tbl_players_stats c
where a.team_id=c.team_id
and b.player_id=c.player_id

many-to-many relationship select checkboxes in form php

I have 3 tables: table with objects (A), table with properties (B) and a table with links object/property (A.id, B.id). The problem is that when I need to edit the object, I need to load data into the form, including the checkboxes state. As one of the possible solutions I see is to select data from the table with properties and from the table with objects and then write down the checkboxes using the nested loops, where the main loop will generate checkboxes and the subloop will be looking through the array of selections and will check the checkbox on id match, but I think there must be some way to select the data from the tables.
An example of data:
A.id B.id C(A.id B.id)
1 1 1 1
2 2 1 2
3 1 3
4 1 4
5
What I want to get is:
B.id A.id
1 1
2 1
3 1
4 1
5 NULL
So the B items with indexes 1,2,3,4 will be checked and 5 is unchecked. I've got this by using
SELECT DISTINCT B.id, A.id FROM B LEFT JOIN C ON B.id=C.id
WHERE A.id=<object id to edit> OR A.id IS NULL GROUP BY B.id
And it actually worked, but only for A.id=1. And with A.id=2 i've got
B.id A.id
5 NULL
Which means for me show only one unselected checkbox for property with id 5.
Instead of something like:
B.id A.id
1 NULL
2 NULL
3 NULL
4 NULL
5 NULL
With A.id=2. Is there any way to achieve this or maybe I should use different logic?
Instead of using WHERE clause, you can specify needed A.id in joining condition.
SELECT DISTINCT B.id, C.a_id FROM B LEFT JOIN C ON B.id=C.b_id AND C.a_id=2 GROUP BY B.id
Try this out here: http://sqlfiddle.com/#!2/68efa/13/0
You can use a CASE without using the WHERE clause
SELECT tableb.`id`,
(CASE WHEN tablec.`aid`=2 THEN tablec.`aid` ELSE NULL END ) AS test
FROM tableb
LEFT JOIN tablec ON (tableb.`id` = tablec.`bid`)
Fiddle for id=2
Fiddle for id=1
It will work and gives you result in all conditions that you have the matched ids or not

Categories