I have 2 tables, a users table and a trade table.
Which look like:
The structure of my code right now is:
<?php
$history = mysqli_query($con, "SELECT * FROM .......");
while($row = mysqli_fetch_array($history)) {
echo("The sentence");
} ?>
Problem I'm facing is that I'm trying to echo the user_name which in one case has to be the receiver and other the person giving it.
Pro tip: Never use SELECT * in software unless you know exactly why you are doing so. In your case it is harmful.
I'm assuming your query is really against the user and trade tables you mentioned in your question.
First, recast your query using 21st century SQL, as follows:
SELECT *
FROM trade AS t
JOIN user AS s ON s.user_id = t.user_id_sender
WHERE s.facebook_id = $fbid
Second, use this to retrieve your user's names and the item id traded.
SELECT s.user_name AS sender,
r.user_name AS receiver,
t.trade_id AS item_id
FROM trade AS t
JOIN user AS s ON s.user_id = t.user_id_sender
JOIN user AS r ON r.user_id = t.user_id_receiver
WHERE s.facebook_id = $fbid
See how we JOIN the user table twice, with two different aliases s (for sender) and r (for receiver)? That's the trick to fetching both names from IDs.
See how we employ the aliases sender and receiver to disambiguate the two user_name columns in the result set?
Now, when you use the php fetch_array function, you'll end up with these elements in the array.
$history['sender']
$history['receiver']
$history['item_id']
The array index strings correspond to the alias names you specified in your SELECT clause in your query.
So, one reason to avoid SELECT * is that you can get more than one column with the same name, and that means fetch_array will eliminate those duplicates and so it will lose useful information from your result set.
Related
I have 2 tables named "rel_eq" and "rel_cat". From the table "rel_cat" I need to compare the id with the column "relcat" in the table "rel_eq". And I need to select all columns from both tables so that I can output in a loop. In some lines in the "rel_eq" table, there is no value in the relcat column. I need to make sure there is either no value where it is missing, or a default, like "uncategory". My code $eq_arr = $wpdb->get_results("SELECT * FROM $table_name CROSS JOIN $table_cat"); but I have to sort through all the id and assign the same id from another table. Please tell me how to do this.
You will need to run the following:
$eq_arr = $wpdb->get_results("SELECT * FROM rel_eq join rel_cat on rel_eq.relcat = rel_cat.id");
If you want a record even when there is no match, use INNER JOIN and handle the problem in your cycle.
I have two database tables that I am using to create a Twitter-style following system.
sh_subscriptions
=> id
=> user_id
=> feed_id
sh_feeds
=> id
=> item
=> shop_name
=> feed_id
The problem with storing feed_id rather than shop_name in sh_subscriptions is that it requires a lot of table joining:
$id = $_POST['id'];
$user_id = $id['id'];
$shop_name = mysqli_escape_string($con, $_POST['shop_name']);
$query = "SELECT * FROM sh_subscriptions s INNER JOIN sh_feeds f ON s.feed_id = f.feed_id WHERE s.user_id = $user_id AND f.shop_name = '$shop_name'";
$result = mysqli_query($con, $query) or die(mysqli_error($con));
if (mysqli_num_rows($result) > 0)
{
$query2 = "DELETE FROM sh_subscriptions s INNER JOIN sh_feeds f ON s.feed_id = f.feed_id WHERE s.user_id = $user_id AND f.shop_name = '$shop_name'";
$result2 = mysqli_query($con, $query2) or die(mysqli_error($con));
}
else
{
// insert the row instead
}
(I know there's an error somewhere in the if statement, but I'll worry about that later.)
If I were to replace feed_id with shop_name, I would be able to replace line 5 with this:
$query = "SELECT * FROM sh_subscriptions WHERE user_id = $user_id AND shop_name = '$shop_name'";
My question is: is it always preferable to store MySQL values as integers where possible, or in a situation like this, would it be faster to have sh_subscriptions contain shop_name rather than feed_id?
Your sh_subscriptions table is actually a many-to-many join table that relates users to feeds. This is considered a fine way to design database schemas.
Your basic concept is this: you have a collection of users and a collection of feeds. Each user can subscribe to zero or more feeds, and each feed can have zero or more subscribers.
To enter a subscription you create a row in the sh_subscriptions table. To cancel it you delete the row.
You say there's "a lot of table joining." With respect, this is not a lot of table joining. MySQL is made for this kind of joining, and it will work well.
I have some suggestions about your sh_subscriptions table.
get rid of the id column. Instead make the user_id and feed_id columns into a composite primary key. That way you will automatically prevent duplicate subscriptions.
add an active column ... a short integer ... to the table. When it is set to a value of 1 your suscription is active. That way you can cancel a subscription by setting active to 0.
you might also add a subscribed_date column if you care about that.
create two compound non unique indexes (active,user_id,feed_id) and (active,feed_id,userId) on the table. These will greatly accelerate queries that join tables like this.
Query fragment:
FROM sh_feed f
JOIN sh_subscription s ON (f.feed_id = s.feed_id AND s.active = 1)
JOIN sh_users u ON (s.user_id = u.user_id)
WHERE f.shop_name = 'Joe the Plumber'
If you get to the point where you have hundreds of millions of users or feeds, you may need to consider denormalizing this table.. that is, for example, relocating the shop name text so it's in the sh_subscriptions table. But not now.
Edit I am proposing multiple compound covering indexes. If you're joining feeds to users, for example, MySQL starts satisfying your query by determining the row in sh_feeds that matches your selection.
It then determines the feed_id, and random-accesses your compound index on feed_id. Then, it needs to look up all the user_id values for that feed_id. It can do that by scanning the index from the point where it random-accessed it, without referring back to the table. This is very fast indeed. It's called a covering index.
The other covering index deals with queries that start with a known user and proceed to look up the feeds. The order of columns in indexes matters: random access can only start with the first (leftmost) column of the index.
The trick to understand is that these indexes are both randomly accessible and sequentially scannable.
one other note If you only have two columns in the join table, one of your covering indexes is also your primary key, and the other contains the columns in the reverse order from the primary key. You don't need any duplicate indexes.
This question already has answers here:
How to resolve ambiguous column names when retrieving results?
(11 answers)
Closed 2 years ago.
I have fields that have the same name in different tables that I'm joining. Such as ticket.status, user.status and transaction.status. At the moment the query returns just status.
How can I get the table name in such a way that it stops similar field names from overwriting and so I can tell the difference between the fields.
Simply put:
$data = array($eventId);
$statement = $this->db->prepare("SELECT * FROM ticket, user, transaction
WHERE ticket.eventId = ?
AND ticket.userId = user.userId
AND ticket.transactionId = transaction.transactionId");
$statement->execute($data);
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
In my research I've found the constant PDO::ATTR_FETCH_TABLE_NAMES that looks like it could help, but I do not know how to implement ( I assume through $statement->setAttribute(); somehow).
I also have concerns that it will not work, as the PHP documentation mentions it is dependent on the driver.
Thanks
Just add new aliases to your select statements
$statement = $this->db->prepare("
SELECT *, ticket.status AS ticket_status, user.status AS user_status, transaction.status AS transaction_status
FROM ticket, user, transaction
WHERE ticket.eventId = ?
AND ticket.userId = user.userId
AND ticket.transactionId = transaction.transactionId
");
Then you can do
$rows[0]['user_status'];
$rows[0]['ticket_status'];
$rows[0]['transaction_status'];
If you are really concern by performance, the quantity of data returned will be greater so instead of adding new aliases you can select every single columns and while you do so put an alias on the status column.
Why not change your to actually join instead:
SELECT
t.status as ticket_status, u.status as user_status, tr.status as trans_status
FROM
ticket as t
inner join user as u on t.userId = u.userId
inner join transaction as tr on t.transactionId = tr.transactionId
where
t.eventId = ?
You don't even need to cast the tables using as something but I find it's neater.
Note, its the casting of the columns that will actually fix this issue, not the join method.
The most obvious comment is "don't do it, that's why aliases exist". But there's still a good underlying question: does MySQL send information about where a result-set column comes from (table, view or calculated)?
Apparently, it does, since the PDOStatement object has an experimental method called getColumnMeta(). I've been testing and it returns an associative array where the table key
contains the source table if column comes from a table or view
is an empty string if the column is calculated
Of course, I'd stick to aliases anyway. Being able to use associative arrays is a killer feature for me.
I am joining 2 tables and trying to display the results. Only problem is every result is duplicated. I have 2 tables, messages and follow. Messages are what a certain user inputs, and I want it to display only to the people that follow that certain user.
Messages | Follow
-id -id
-message -mem1 (logged in user)
-userid -mem2 (followed user)
-created
$display ="";
$sql = mysql_query("
SELECT * FROM messages AS me
JOIN follow AS fl
ON me.userid = fl.mem2
WHERE fl.mem1 = $id (logged in user)
ORDER BY me.created
DESC LIMIT 10
") or die(mysql_error());
while($row=mysql_fetch_array($query)){
$msgid = $row["id"];
$message = $row["message"];
$userid = $row["userid"];
$created = $row["created"];
$display .="<?php echo $userid; ?> : <?php echo $message; ?><br />
<?php echo $created; ?>";
}
In the database there are no duplicates, just on the retrieve. Thanks for the input!
Edited: Display Code
You're getting "double" results, most likely because the query results in something different then you expect.
If I understand your table-structure correctly; you have a one-to-many relation from messages to followers.
In your query, however, you fetch combinations of messages and followers. Each line will consist of a unique combination of message<>follower.
In short; when a single message has two followers, you'll get two rows in the result with the same message; but a different follower entry.
If you want to show each message once; and then list all followers per message you can either use group-by functions (e.g group_concat) and group-by on message entries. The other possibility is to fetch the followers in a separate query once you've retrieved the message row, and then print the results from that query as the followers for that message.
If you're simply trying to get the number of followers; you can use a group-by on the UID of your message table and add a count on the UID or user ID of the follower table. (Do not that with group-by, the select * from shouldn't be used; but separate columns can.)
There's really only a few things that could cause the records to duplicate - try breaking down the query into basic components to see if there are more than one record:
SELECT * FROM follow WHERE mem1 = [id];
SELECT * FROM messages WHERE userid = [mem2 from previous result];
If either of the previous statements return more than one record, than the problem lies there. Other than that, I'd look at the PHP code to see if you're doing something there.
As for the query itself, I have a few recommendations:
Place the table with the filter first - the sooner you can narrow the results the better.
Specify a field list instead of using '*' - this will be a tiny bit more efficient, and clarify what you're after. Also, it will give 'DISTINCT' a fighting chance to work...
Here's an example:
SELECT DISTINCT me.id, me.message, me.userid, me.created
FROM follow AS fl
INNER JOIN messages AS me ON me.userid = fl.mem2
WHERE fl.mem1 = :logged_in_user
ORDER BY me.created
DESC LIMIT 10
If you are sure that there are no duplicates and the problem is in the query (you can check that by executing it from your database's interface), you can try two things:
Use the follow table as the leading one:
SELECT messages.*
FROM follow
JOIN messages ON follow.mem2=messages.userid
WHERE follow.mem1=$id
ORDER BY messages.created DESC
LIMIT 0,10;
Use a subquery:
SELECT *
FROM messages
WHERE userid IN(
SELECT DISCTINCT(mem2)
FROM follow
WHERE mem1=$id
)
ORDER BY created DESC
LIMIT 0,10;
Excuse me for what I'm sure is an elementary question for most of you, but I have an issue with table columns from separate tables having the same name as one another, and trying to select from both tables in the same query.
Okay, so this is my code:
$q_value = $mdb2->quote($_POST['query']);
$field = $_POST['field'];
$sql = "SELECT m.*, l.name FROM memberlist m, mail_lists l
WHERE m.$field=$q_value
AND l.id = m.list
ORDER BY m.id";
$l_list = $mdb2->queryAll($sql, '', 'MDB2_FETCHMODE_ASSOC');
The table memberlist has the following columns:
id, email, list, sex, name
and the table mail_lists has the following columns:
id, name
After running the query, I later loop through the results with a foreach like so:
foreach ($l_list as $l){ //blahblah }
The problem is that the column 'name' in mail_lists refers to the names of the list, while the column 'name' in memberlist refers to the name of the member.
When I later access $l->name (within the foreach), will I get m.name, or l.name? Furthermore, how do I get access to the other?
Or will I just have to do two separate queries?
Why can you not simply use:
SELECT m.*, l.name as l_name FROM ...
and then distinguish between name and l_name?
And this is a matter of style, so others may disagree but I never use * in queries, tending to prefer explicit column specifications. That's not actually a problem in your case other than if you wanted to do:
SELECT m.*, l.* FROM ...
and still distinguish between the two names. With explicit column specifications, you can add an as clause to each column to give then unique names.