MySQL: Join (2 tables) vs single queries (1 table) - php

In PHP, I have an array of 11 persons where just the ID of each person is given:
$persons = array(1, 3, 39, 72, 17, 20, 102, 99, 77, 2, 982);
In my MySQL database, there's a table containing detailed information about each person:
TABLE personInfo
- ID (integer)
- name (string)
- birth date (timestamp)
- weight (decimal)
- ...
PROBLEM:
So now, I want to select the matching name for each ID in the PHP array. I can only imagine two solutions to do this:
1. for-loop:
foreach ($persons as $person) {
$result = mysql_query("SELECT name FROM personInfo WHERE id = ".$person);
}
2. logical operator OR
$result = mysql_query("SELECT name FROM personInfo WHERE id = 1 OR id = 3 OR id = 39 OR ...");
Both solutions are slow, aren't they?
But if I had another MySQL table containing the IDs of the PHP array ...
TABLE ids
- ID (integer)
... I could use a join to make a really fast MySQL query, right?
$result = mysql_query("SELECT a.ID, b.name FROM ids AS a JOIN personInfo AS b ON a.ID = b.ID");
QUESTION:
Is all this correct so far? If yes: Why is this so? The MySQL query is faster if I have a second table? With only one table it is incredibly slow? What is the fastest way to solve my problem (selecting the names matching the IDs of the PHP array)?

If you have lots of ids (several hundreds or more), feeding the values into a temporary table and joining it is actually faster.
You may want to read this article:
Passing parameters in MySQL: IN list vs. temporary table

IF you have ID's you should just query the Id's you have. So
$result = mysql_query("SELECT name FROM personInfo WHERE id = 1 OR id = 3 OR id = 39 OR ...");
would be right, although I would use
$result = mysql_query("SELECT name FROM personInfo WHERE id IN (1,2,3,4....) )

Related

Compare values of a PHP array with a large table MySQL

Ok, normally I would do something like this in PHP if I compared just few values:
// I want to get 1
$a = array(1,2);
$b = array(2);
// It produces 1
$result = array_diff($a, $b);
But in this case, $b is a large table in MySQL with millions of rows and retrieving it to PHP would be crazy, so my DBMS (MySQL) must do the work.
How could I do something like:
-- I need treating $a as a column
SELECT $a NOT IN (SELECT id FROM b);
Example
table B
|id|
2
3
4
array A
array(1, 2, 3, 5);
Result
A - B = A - INTERSECT(A,B) = [1, 5]
Example 2
This is exactly what I need for SQL server. Any ideas to do something similar in MySQL?
Note: A has about 5000 ids, so:
SELECT 1
UNION SELECT 1
UNION SELECT 1
UNION SELECT 2
UNION SELECT 5
...
...produces #1064 - memory exhausted
You can join the variables in the array $a in PHP and use that in your SQL query to retrieve the data, It would efficient in you case.
$in = join(',', array_fill(0, count($a), '?'));
$sql = "
SELECT *
FROM galleries
WHERE id IN ($in)"
Here is a working fiddle to prove that the SQL query will return the desired output.
EDIT :
According your new requirement, You could do the array comparison in
the PHP side because You have to somehow bring up the result from
MySQL to eliminate the result from PHP array. If you retrieve the
array $a from another table then we could have done something like a
join. But in this case we cannot do it. Also you mentioned that the
MySQL table has large number of rows so I assume PHP array will anyway
remove only couple of items from the results after bringing it to the
memory.
If you want to check the values exist or not, you can try this one
<?php
$some_id = "100";
$id = mysql_real_escape_string($some_id); // i use escape to compare IP ADDRESSES. you can remove it
$result = mysql_query("SELECT id FROM your_table WHERE id = '$id'");
if (!mysql_num_rows($result))
{
echo "Not Exists";
}
else
{
echo "Exists";
}
?>
I hope this will help
Consider using the JOIN NULL query:
strSQL = "SELECT t2.id
FROM tableB t1
RIGHT JOIN
(SELECT id
FROM tableB
WHERE id IN (".implode(',',$arrayA).")
) t2
ON t1.id = t2.id
WHERE t1.id IS NULL";
Also, depending on how large $arrayA is, consider appending to temp table and join above, replacing sub query with temp table.

MySQL Loop from two different Tables

i want to get values from two different tables.
my problem is the table have no relation to each other but the "team_id"
i have a "player" and a "player_stat" table.
my result should look like:
Playername (from the "player" table)
Points: 8, Assists: 2, Fouls: 0 (from the "player_stat" table)
Playername 2 (from the "player" table)
Points: 8, Assists: 2, Fouls: 0 (from the "player_stat" table)
Here is my try:
<?php
/* PLAYERSTAT */
$attrs = array(PDO::ATTR_PERSISTENT => true);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("SELECT data.player_stat
FROM
(SELECT * from player_stat
UNION
SELECT * from player) data
");
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
?>
echo $row["playername"]."<br>";
echo $row["points"]." ".$row["assists"];
<?php }
?>
my tables
player:
id
team_id
name
created
player_stat:
id
team_id
points
assists
fouls
created
Your SQL query could look like this (supposed the team_id is supposed to connect the stats and the player):
SELECT
player.playername,
player_stat.points,
player_stat.assists,
player_stat.fouls
FROM
player
LEFT JOIN player_stat ON (player.team_id = player_stat.team_id)
But it's a little bit strange, that the only relation between your tables is the team_id. If the output should be per player you have to introduce the relation between the stats and the player.
Say, your player can be in a team, there should be a team_id and a player_id. And your JOIN would then read:
LEFT JOIN player_stats ON (player.player_id = player_stats.player_id)
and as addition you can query for your team:
WHERE
player.team_id = 55
or you can add other joins so that you get your team details and so on.
Modify your select statement to read:
SELECT * FROM player, player_stat WHERE player.team_id = player_stat.team_id
The above is based on your statement that they have the team_id in common. If this is not what you are trying to achieve please refine your question and post the database schema.

PHP MySQL - Find Row ID

I have a table called "participants" that has 3 fields:
prt_id
prt_event_id
prt_participant_id
What I have is a select query with a where condition on event_id. The query returns let's say 20 rows (20 different participants). What I would like to do is to be able to figure out the row number for a given participant (prt_id).
SELECT *
FROM participants
WHERE prt_id = someinteger
While you can't specifically find a row ID using MySQL, you could do something like the following:
$conn = new mysqli(/*dbinfo*/);
$res = $conn->query("SELECT prt_id FROM participants");
$rowids = array(); $currid = 1;
while ($row = $res->fetch_object()) { // this is using the mysqli library
$rowids[$row->prt_id] = $currid;
$currid++;
}
This would give you an array of ids associated with prt_id.
You could do something like:
<?php
$counter = 1; // Start at one for first entry
$res = mysql_query("SELECT * FROM participants WHERE prt_id = 12");
while( $array = mysql_fetch_assoc($res) )
{
// Do something with the counter, store it into array with details
$counter++;
}
?>
This should do what you want inside MySQL (ie assign a rownum in the order of prt_id), but the performance will be dependent on the number of rows in the table so it's not optimal.
SELECT * FROM (
SELECT #tmp:=#tmp+1 rownum, p.*
FROM (SELECT #tmp:=0) z, participants p
ORDER BY prt_id
) participants
WHERE prt_id = 36;
Demo here.
Edit: This "doh level" rewrite uses an simple index range instead of a table scan, so should be much faster (provided prt_id is a PRIMARY KEY)
SELECT *, COUNT(p2.prt_id) ROWNUM
FROM participants p1
JOIN participants p2
ON p1.prt_id >= p2.prt_id
WHERE p1.prt_id=36;
Demo here.
you could just add an index column in your database, set it as int, primary key and auto increment. then when retrieving the row you retrieve the index number.
RowID is a feature of Oracle: http://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns008.htm.
MySQL does not have something like that, you can basically emulate that by assign number to an array inside php as you retrieve each row, but that doesn't guarantee you the same number next time you retrieve that results. You probably have to settle for using one of the primary IDs

sql using LIKE clause : php

I'm trying to generate a list of events that a user is attending. All I'm trying to do is search through columns and comparing the userid to the names stored in each column using LIKE.
Right now I have two different events stored in my database for testing, each with a unique eventID. The userid i'm signed in with is attending both of these events, however it's only displaying the eventID1 twice instead of eventID1 and eventID2.
The usernames are stored in a column called acceptedInvites separated by "~". So right now it shows "1~2" for the userid's attending. Can I just use %like% to pull these events?
$userid = $_SESSION['userid'];
echo "<h2>My Events</h2>";
$myEvents = mysql_query("select eventID from events where acceptedInvites LIKE '%$userid%' ");
$fetch = mysql_fetch_array($myEvents);
foreach($fetch as $eventsAttending){
echo $eventsAttending['eventID'];
}
My output is just 11 when it should be 12
Change your table setup, into a many-to-many setup (many users can attend one event, and one user can attend many events):
users
- id (pk, ai)
- name
- embarrassing_personal_habits
events
- id (pk, ai)
- location
- start_time
users_to_events
- user_id ]-|
|- Joint pk
- event id ]-|
Now you just use joins:
SELECT u.*
FROM users u
JOIN users_to_events u2e
ON u.id = u2e.id
JOIN events e
ON u2e.event_id = e.id
WHERE u.id = 11
I'm a bit confused by your description, but I think the issue is that mysql_fetch_array just returns one row at a time and your code is currently set up in a way that seems to assume $fetch is filled with an array of all the results. You need to continuously be calling mysql_fetch_array for that to happen.
Instead of
$fetch = mysql_fetch_array($myEvents);
foreach($fetch as $eventsAttending){
echo $eventsAttending['eventID'];
}
You could have
while ($row = mysql_fetch_array($myEvents)) {
echo $row['eventID'];
}
This would cycle through the various rows of events in the table.
Instead of using foreach(), use while() like this:
$myEvents = mysql_query("SELECT `eventID` FROM `events` WHERE `acceptedInvites` LIKE '".$userid."'");
while ($fetch = mysql_fetch_array($myEvents))
{
echo $fetch['eventID'];
}
It will create a loop like foreach() but simpler...
P.S. When you make a MySQL Query, use backticks [ ` ] to ensure that the string is not confused with MySQL functions (LIKE,SELECT, etc.).

Summing a field from all tables in a database

I have a MySQL database called "bookfeather." It contains 56 tables. Each table has the following structure:
id site votes_up votes_down
The value for "site" is a book title. The value for "votes_up" is an integer. Sometimes a unique value for "site" appears in more than one table.
For each unique value "site" in the entire database, I would like to sum "votes_up" from all 56 tables. Then I would like to print the top 25 values for "site" ranked by total "votes_up".
How can I do this in PHP?
Thanks in advance,
John
You can do something like this (warning: Extremely poor SQL ahead)
select site, sum(votes_up) votes_up
from (
select site, votes_up from table_1
UNION
select site, votes_up from table_2
UNION
...
UNION
select site, votes_up from table_56
) group by site order by sum(votes_up) desc limit 25
But, as Dav asked, does your data have to be like this? There are much more efficient ways of storing this kind of data.
Edit: You just mentioned in a comment that you expect there to be more than 56 tables in the future -- I would look into MySQL limits on how many tables you can UNION before going forward with this kind of SQL.
Here's a PHP code snip that should get it done.
I have not tested it so it might have some typos and stuff, make sure you replace DB_NAME
$result = mysql_query("SHOW TABLES");
$tables = array();
while ($row = mysql_fetch_assoc($result)) {
$tables[] = '`'.$row["Tables_in_DB_NAME"].'`';
}
$subQuery = "SELECT site, votes_up FROM ".implode(" UNION ALL SELECT site, votes_up FROM ",$tables);
// Create one query that gets the data you need
$sqlStr = "SELECT site, sum(votes_up) sumVotesUp
FROM (
".$subQuery." ) subQuery
GROUP BY site ORDER BY sum(votes_up) DESC LIMIT 25";
$result = mysql_query($sqlStr);
$arr = array();
while ($row = mysql_fetch_assoc($result)) {
$arr[] = $row["site"]." - ".$row["sumVotesUp"];
}
print_r($arr)
The UNION part of Ian Clelland answer can be generated using a statement like the following. The table INFORMATION_SCHEMA.COLUMNS has a column TABLE_NAME to get all tables.
select * from information_schema.columns
where table_schema not like 'informat%'
and column_name like 'VOTES_UP'
Join all inner SELECT with UNION ALL instead of UNION. UNION is doing an implicit DISTINCT (on oracle).
The basic idea would be to iterate over all your tables (using a SQL SHOW TABLES statement or similar) in PHP, then for every table, iterate over the rows (SELECT site,votes_up FROM $table). Then, for every row, check the site against an array that you're building with sites as keys and votes up as values. If the site is already in the array, increment its votes appropriately; otherwise, add it.
Vaguely PHP-like pseudocode:
// Build an empty array for use later
$votes_array = empty_array();
// Get all the tables and iterate over them
$tables = query("SHOW TABLES");
for($table in $tables) {
$rows = query("SELECT site,votes_up FROM $table");
// Iterate over the rows in each table
for($row in $rows) {
$site = $row['site'];
$votes = $row['votes_up'];
// If the site is already in the array, increment votes; otherwise, add it
if(exists_in_array($site, $votes_array)) {
$votes_array[$site] += $votes;
} else {
insert_into_array($site => $votes);
}
}
}
// Get the sites and votes as lists, and print out the top 25
$sorted_sites = array_keys($votes_array);
$sorted_votes = array_values($votes_array);
for($i = 0; $i < 25; $i++) {
print "Site " . $sorted_sites[$i] . " has " . $sorted_votes[$i] . " votes";
}
"I allow users to add tables to the database." - I hope all your users are benevolent and trustworthy and capable. Do you worry about people dropping or truncating tables, creating incorrect new tables that break your code, or other things like that? What kind of security do you have when users can log right into your database and change the schema?
Here's a tutorial on relational database normalization. Maybe it'll help.
Just in case someone else that comes after you wants to find what this could have looked like, here's a single table that could do what you want:
create database bookfeather;
create user bookfeather identified by 'bookfeather';
grant all on bookfeather.* to 'bookfeather'#'%';
use bookfeather;
create table if not exists book
(
id int not null auto_increment,
title varchar(255) not null default '',
upvotes integer not null default 0,
downvotes integer not null default 0,
primary key(id),
unique(title)
);
You'd vote a title up or down with an UPDATE:
update book set upvotes = upvotes + 1 where id = ?
Adding a new book is as easy as adding another row:
insert into book(title) values('grails in action')
I'd strongly urge that you reconsider.

Categories