So I have same problems with my query. I havent use JOIN so far so I am pretty sure I have something wrong with it but not sure. I have tried this for hours and can not understand - It seems that code is correct but I do not get desired results.
Here are my two tables:(wrote them by hand so If there is some error dont bother)
CREATE TABLE messages (
msg_id INT(11) PRIMARY KEY,
sender_id INT(11),
msg text,
conversation_id INT(11),
seen datetime,
)
CREATE TABLE conversations (
conversation_id INT(11) PRIMARY KEY,
user1_id INT(11),
user2_id INT(11),
requested datetime,
)
I am working now on notification module. I want to get list of conversation_id where user with $user_id have at least one unread message so that I could notify to him that you have new messages in conversation #:
public function getMsgNotifications($user_id)
{
$this->db->select('conversations.conversation_id');
$this->db->distinct('conversations.conversation_id');
$this->db->from('conversations');
$this->db->join('messages','conversations.conversation_id = messages.conversation_id','INNER');
$this->db->where('conversations.user1_id', $user_id);
$this->db->or_where('conversations.user2_id', $user_id);
$this->db->where('messages.sender_id !=', $user_id);
$this->db->where('messages.seen', '');
$query = $this->db->get();
return $query->result();
}
However I get absolutelly random results. I have spent hours looking at values in database and trying to understand problem but the results I get has no patern.
Would appreciate if someone could point out where there are mistakes in my query.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am checking the for the user_id (it is held in a session) - this is working. Then I am running a SELECT query for that user for the database table click_count. I am checking to see if that user has any records within it, ie: $page_count. If not, I want my INSERT statement to run to add that user to the database table along with other data.
The part I do not understand is it seems that my UPDATE query is always running. For example no matter which user I login with my query only updates the only user in the database table. IE: Bob is the only user in the click_count table, if I log in with Pete, Bob's record is being updated.
I have tested the value for $page_count and it equals 0, so my INSERT should be running. I have also tried if ($page_count === 0) {
Does anyone see anything I am missing?
$curPage = $_SERVER['PHP_SELF'];
$clicks = 0;
$setup = 0;
$page_total_count = 0;
var_dump($user_id);
$click_sql = "
SELECT *
FROM click_count
WHERE user_id = ?
AND page_url = ?
";
$click_stmt = $con->prepare($click_sql);
$click_stmt->execute(array($user_id, $curPage));
$click_stmt_rows = $click_stmt->fetchAll(PDO::FETCH_ASSOC);
$page_count = $click_stmt->rowCount();
foreach ($click_stmt_rows as $click_stmt_row) {
$setup_status = $click_stmt_row['setup'];
$page_total_count = $click_stmt_row['page_count'];
}
if ($page_count == 0) {
$click_insert_sql = "
INSERT INTO click_count
(user_id, page_url, page_count, setup)
VALUES(?, ?, ?, ?)
ON DUPLICATE KEY UPDATE page_count=page_count+1;
";
$click_insert_stmt = $con->prepare($click_insert_sql);
$click_insert_stmt->execute(array($user_id, $curPage, 1, $setup));
}
else {
$click_update_sql = "
UPDATE click_count
SET page_count=page_count+1
WHERE user_id = ?
AND page_url = ?
";
$click_update_stmt = $con->prepare($click_update_sql);
$click_update_stmt->execute(array($user_id, $curPage));
}
Table
click_count
CREATE TABLE `click_count` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`page_url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`page_count` int(11) NOT NULL,
`setup` int(5) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`),
UNIQUE KEY `page_url` (`page_url`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Since there is only the one user in the table, there is no record "to insert/update", therefore
ON DUPLICATE KEY UPDATE failed you silently.
A regular UPDATE will suffice:
I.e. and as an example:
UPDATE table SET col_x = 0|1 WHERE col_y = ? // (boolean 0-1)
Note:
If ever you wish to increase a column by counting later on, the syntax would be:
UPDATE table SET col_x = col_x + 1 WHERE col_y = ?
In regards to your asking about how you could improve on your code:
#Fred-ii- Thanks. Yes, it is working now how I want, but if there are ways to improve the code I am always willing to try to learn it. I just remembered people in the past saying that I didn't need the update query at all with the duplicate key update. – Paul
You could use named placeholders :name rather than ? since they are easier to keep track of, but this is of course a matter of opinion that I feel is also shared by many and not just myself.
Footnotes/credits:
I would like to also give credit to the following comment:
"If you always fall into update indicates that $page_count is not zero.. Try to echo() it to see maybe.. I would probably first try to add another user into click_count table and then it may become easier to see where it goes wrong.. – johnyTee"
where the OP responded with:
"#Fred-ii- I figured it out. I used johnyTee's advise and tried adding another user to the database manually and it wouldn't let me because of the unique index for the page_url column. I then removed the unique index from it and now it works perfectly. Thanks for the help! – Paul"
from PHP PDO doc http://php.net/manual/en/pdostatement.rowcount.php
PDOStatement::rowCount() returns the number of rows affected by a
DELETE, INSERT, or UPDATE statement.
if you need th number of rows in select you should use somethings like
$sql = "SELECT *
FROM click_count
WHERE user_id = ?
AND page_url = ?
";
$result = $con->prepare($sql);
$result->execute();
$number_of_rows = $result->fetchColumn();
It may be '0' (a string). You can use intval to convert it to an integer.
$page_count = intval( $click_stmt->rowCount() );
http://php.net/manual/en/function.intval.php
For most databases, PDOStatement::rowCount() does not return the number of rows affected by a SELECT statement. Instead, use PDO::query() to issue a SELECT COUNT(*) statement with the same predicates as your intended SELECT statement, then use PDOStatement::fetchColumn() to retrieve the number of rows that will be returned. Your application can then perform the correct action.
try like this:
$sql = "SELECT count(*)
FROM click_count
WHERE user_id = ?
AND page_url = ?
";
if ($res = $conn->query($sql)) {
/* Check the number of rows that match the SELECT statement */
if ($res->fetchColumn() > 0) {
//insert
}else {
//update
}
}
I need to display just the last date entry for a person in a table, taken from joined tables.
I only seem to able to display the first entry or all.
Controller
public function session_view($id)
{
$data['main_view'] = 'session_view';
$data['view'] = $this->Swim_model->CRUD_read_session($id);
$this->load->view('load_view',$data);
}
Model
public function CRUD_read_session($sessionid)
{
return $this->db->select('*')
->from('sessionsandswimmers')
->join('child', 'ID = SwimmersID')
->join('swimmersawards', 'PersonID = ID')
->join('awards', 'AwardID = AwardsID')
->order_by('LastName')
->order_by('DateAwarded')
->where('SessionID', $sessionid)
->get();
}
View
foreach ($view->result() as $row)
{
echo '<tr>'.$row->FirstName.'</td><td'.$row->LastName.'</td><td>'.$row->Description.'</td><td>'.$row->DateAwarded.'</td></tr>';
}
Result
As you can see, there are several entries for each person (except 1st).
I need to display just the latest date entry for each person.
So there should only be 4 entries.
Table structure
sessionsandswimmers - each session has 4 swimmers
1 tempid Primary int(11) No None AUTO_INCREMENT
2 SessionID int(11) No None
3 SwimmersID int(11) No None
4 SessionSeasonID int(11) No None
5 Year int(11) No None
6 LocationSS int(11) No None
child - gets the swimmers name
swimmersawards - multiple entries per child
1 PersonID int(11) No None
2 AwardsID int(11) No None
3 DateAwarded date No None
awards - gets the name of the award
change ->order_by('DateAwarded') to ->order_by('DateAwarded','DESC') . Need to specify which type of ordering you want. And for just one entry per person use :
->group_by('FirstName,LastName')
If I understood correctly, the result you displayed is the table structure. Where you want the latest entry by date for each "name". If this is right then follow,
SELECT DISTINCT `name` FROM `<your-joined-tables>` order by `date` DESC
Sorry I am not good with CI's query builder.
Hope it works.
I have a question if anyone can answer. Please excuse my inexperience with this, but this is my first project that I have attempted and all of this is really new to me. I am in the process of trying to build an inventory system at work using php and mySQL and I have hit a bit of a wall regarding how I am going to display the items that are currently loaned out to people.
I have the items that are being provisioned to users broken down into 4 categories and records of the loans for these items are stored into 4 different tables. I also have another table for users, as well as tables for the items, and their characteristics.
What I want when my page is displayed to to have all of the items that are assigned to each user grouped together in a table. I have two ideas on how I can do this, but I'm not sure which would be the best way.
My first thought was to pull all of the users from the users table and store the information into an array, then pull all of the information from the 4 loan tables and store each table into an array. From there I would do something like
for($i=1;$i>sizeof($usersArray);$i++){
for($a=1;$a>sizeof($loanTable1Array);$a++){
if($userArray[$i][userID] == $loanTable1Array[$a][userID]{
//list items
}
}
for($b=1;$b>sizeof($loanTable2Array);$b++){
if($userArray[$i][userID] == $loanTable2Array[$b][userID]{
//list items
}
}
for($c=1;$c>sizeof($loanTable3Array);$c++){
if($userArray[$i][userID] == $loanTable3Array[$c][userID]{
//list items
}
}
for($d=1;$d>sizeof($loanTable4Array);$d++){
if($userArray[$i][userID] == $loanTable4Array[$d][userID]{
//list items
}
}
}
My concern with this though is that I will have around 100-150 users and each table will have an average of 100 different items. This would mean around 40,000 - 60,000 iterations of the loop.
My other idea was to do pull all of the entries from the user table, then use that data to query the other 4 tables using the userID in a where statement like this. But then I read that if you have a query in a loop then you're doing it wrong.
$sql = "SELECT userID FROM users";
$allUsers = runQuery($sql); //data is sanitized before running the query
for($i = 1; $i<sizeof($allUsers); $i++){
$loan1sql = "SELECT * FROM loantable1 WHERE userID = {$allUsers[$i][$userID]}'";
$loan1Items= runQuery($loan1sql);
for($a = 1; $a<sizeof($loan1Items); $a++){
//list items
}
$loan2sql = "SELECT * FROM loantable2 WHERE userID = '{$allUsers[$i][$userID]}'";
$loan2Items= runQuery($loan2sql);
for($b = 1; $b<sizeof($loan2Items); $b++){
//list items
}
$loan3sql = "SELECT * FROM loantable3 WHERE userID = '{$allUsers[$i][$userID]}'";
$loan3Items= runQuery($loan3sql);
for($c = 1; $c<sizeof($loan3Items); $c++){
//list items
}
$loan4sql = "SELECT * FROM loantable4 WHERE userID = '{$allUsers[$i][$userID]}'";
$loan4Items= runQuery($loan4sql);
for($d = 1; $d<sizeof($loan1Items); $d++){
//list items
}
}
Doing this would result in 400 - 600 calls to the database each time the page is loaded. Does anyone have any input on what my best course of action would be? Any help would be greatly appreciated.
By considering an extra category column , you could have one loantable instead of four . Then you would just use one query by JOINing the tables .
Just an example showing one way to do it :
-- Table structure for table `users`
CREATE TABLE IF NOT EXISTS `users` (
`userID` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`userID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
-- Dumping data for table `users`
INSERT INTO `users` (`userID`) VALUES
(1),
(2);
-- --------------------------------------------------------
-- Table structure for table `loantable`
CREATE TABLE IF NOT EXISTS `loantable` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`category` int(11) NOT NULL,
`userID` int(11) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
-- Dumping data for table `loantable`
INSERT INTO `loantable` (`ID`, `category`, `userID`) VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 1),
(4, 1, 2),
(5, 3, 2);
Then you would use just one query like :
SELECT *
FROM
`users`
LEFT OUTER JOIN loantable ON loantable.userID = users.userID
WHERE 1
ORDER BY
users.userID
,category
(refer to answers above. This was too long to add as a comment, but I thought it would be helpful)
#cartalot and #Uours THANK YOU!!!! - I had considered creating one table for all of the loans early on but didn't know how to implement it. This makes perfect sense though. My whole issue was confusing the foreign key - parent key constraints in mySQL with how you can actually join tables to display information on your page.
Not to sound like a complete moron, but I think this might be constructive to someone that reads this down the road. I got confused by how you can create fk - pk relations in myPHPAdmin and what they actually do. I though that these relations were necessary to join tables (obviously wrong). I saw the visual connections and though that these tables were somehow "connected".
I know understand that when you create a foreign key parent key restraints all you are basically doing is limiting they data that you can enter into a table based on what is in another table. You can still join information from different tables without these constraints.
I've got a many-to-many relationship between Person and Interests. This means that I had to create a middle table called person_interests.
The Person table was created using:
create table if not exists Person
(
id int not null auto_increment primary key,
username varchar(10) not null,
name varchar(40) not null,
gender varchar(6) not null,
dateOfBirth timestamp not null,
signUpDate timestamp not null,
email varchar(40) not null
);
The interests table was created using:
create table if not exists interests
(
id int not null auto_increment primary key,
interestName varchar(10) not null
);
insert into interests(id, interestName)
values
(1, 'sport'),
(2, 'computer'),
(3, 'dancing'),
(4, 'boating'),
(5, 'car');
and person_interests was created using:
create table if not exists person_interests
(
id int not null auto_increment primary key,
UserID int not null,
foreign key (UserID) references Person(id),
Interestid int not null,
foreign key (Interestid) references interests(id)
);
I'm trying to select entries from the person_interests table but with no luck. My PHP function for doing this is:
function get_person_interests($id)
{
$connection = mysql_open();
$query = "select pi.UserID, i.interestName from interests as i, person_interests as pi, where i.id = pi.interestid";
$result = mysql_query($query, $connection) or show_error();
$person_interests = array();
while($person_interest = mysql_fetch_array($result))
{
$person_interests = $person_interest;
}
mysql_close($connection) or show_error();
return $person_interests;
}
But this doesn't work! This is what my template (user_detail.tpl) looks like:
{foreach $interests as $interest}
<li>{$interest.interestName}</li>
{/foreach}
and this is what my user_detail.php looks like:
$id = $_GET['id'];
$Person = get_person($id);
$interests = get_person_interests($id);
$smarty = new Smarty;
$smarty->assign("Person", $Person);
$smarty->assign("interests", $interests);
$smarty->display("user_detail.tpl")
I'm trying to display the interests that the user chose upon signup. They are stored in the person_interests table. Every time I try to go to the user_detail.php page it gives me this error:
Error 1064 : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where i.id = pi.interestid' at line 1
If anybody can help me then that would be much appreciated!
SQL syntax error. You shouldn't have a comma before the where clause. I would suggest that you write your query as a join though:
$query = "select pi.UserID, i.interestName from person_interests as pi join interests as i on i.id = pi.interestid";
Of course, if you want the interests of a particular person (Which your code suggests), you need to filter on the user_id:
$query = "select pi.UserID, i.interestName from person_interests as pi join interests as i on i.id = pi.interestid where pi.UserID =" . mysql_escape_string($id);
In reply to your comment:
I think you want to replace this line:
$person_interests = $person_interest;
With:
$person_interests[] = $person_interest;
Looks like a typo. You have "Interestid" in your table, but "interestid" in your query. Note the case difference. And you have a comma after your where. You should rewrite this using a join by doing:
select
pi.UserId,
i.interestname
from
person_interests pi
left join
interests i
on
pi.Interestid = i.id
You have an error in your SQL query which you should debug first.
Connect to your database with a commandline client (the command is named mysql).
Then write the SQL query in a text editor. Then copy the query and paste it into the commandline client to run it.
The commandline client will always tell you where the error is. You can then read the SQL statement in the MySQL manual and look where you've made a syntax error or something similar.
Then you can adopt the query in your editor accordingly and try again.
Repeat until your query has no error any longer and gets you the expected results.
Then copy the working query into your application and replace the invariants with the variables. Take care that you properly escape the values.
I have a string of 3 queries that are designed to
Find which messages have other messages with the same id which represents replies
Find which messages of the results from the first query have the specified user as entering the first message of that string of messages (min timestamp)
Find the latest message of that string of messages (max timestamp)
The problem comes with the third query. I get the expected results up to the second query, then when the third is executed, without the MAX(timestamp) as max, I get the expected results. When I add that, I only get the first message for each string of messages when it should be the last, regardless of whether I use min or max and the row count says 1 row returned when there is 2 rows shown. Anyone got any ideas on where I went wrong?
$sql="SELECT reply_chunk_id
FROM messages
GROUP BY reply_chunk_id
HAVING count(reply_chunk_id) > 1 ";
$stmt16 = $conn->prepare($sql);
$result=$stmt16->execute(array('specified_user'));
while($row = $stmt16->fetch(PDO::FETCH_ASSOC)){
$sql="SELECT user,reply_chunk_id, MIN(timestamp) AS grp_timestamp
FROM messages WHERE reply_chunk_id=?
GROUP BY reply_chunk_id HAVING user=?";
$stmt17 = $conn->prepare($sql);
$result=$stmt17->execute(array($row['reply_chunk_id'],'specified_user'));
while($row2 = $stmt17->fetch(PDO::FETCH_ASSOC)){
$sql="SELECT message, MAX(timestamp) as max FROM messages WHERE reply_chunk_id=?";
$stmt18 = $conn->prepare($sql);
$result=$stmt18->execute(array($row2['reply_chunk_id']));
while($row3 = $stmt18->fetch(PDO::FETCH_ASSOC)){
echo '<p>'.$row3['message'];
}
}
}
echo ' '.$stmt18->rowCount();
Create table view of messages, as requested:
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`timestamp` int(11) NOT NULL,
`user` varchar(25) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'anonimous',
`message` varchar(2000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`topic_id` varchar(35) NOT NULL,
`reply_chunk_id` varchar(35) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
Since message isn't grouped, exactly which message from the group you'll get isn't defined. If you want the message with the maximum timestamp, you'll need to explicitly select it:
SELECT message, timestamp AS max
FROM messages
WHERE reply_chunk_id=:rcid
AND timestamp=(SELECT MAX(timestamp)
FROM messages
WHERE reply_chunk_id=:rcid)
or:
SELECT message, timestamp AS max
FROM messages
WHERE reply_chunk_id=?
ORDER BY timestamp DESC, id
LIMIT 1
The second query breaks ties (in the very unlikely but possible situation that more than one person posts at the same time) by also selecting the message with the highest id.
General feedback
Many of the variables you set within the loops are invariant, and thus should be moved outside the loop.
$stmt17 will return at most 1 result. Moreover, $stmt18 will return always return exactly one result. Rewriting the second inner while loop (for $stmt17) as an if statement, and simply fetching the result from $stmt18 would be equivalent and clearer as to purpose.
try {
$threadSql="SELECT reply_chunk_id
FROM messages
GROUP BY reply_chunk_id
HAVING count(reply_chunk_id) > 1 ";
$firstUserSql="SELECT user, MIN(timestamp) AS grp_timestamp
FROM messages WHERE reply_chunk_id=?
GROUP BY reply_chunk_id HAVING user=?";
$lastMsgSql="SELECT message, MAX(timestamp) as max FROM messages WHERE reply_chunk_id=?";
$threadQuery = $conn->prepare($threadSql);
$threadQuery->setFetchMode(PDO::FETCH_ASSOC);
$firstUserQuery = $conn->prepare($firstUserSql);
$lastMsgQuery = $conn->prepare($lastMsgSql);
$result=$threadQuery->execute(array('specified_user'));
foreach ($threadQuery AS $thread){
$result=$firstUserQuery->execute(array($thread['reply_chunk_id'],'specified_user'));
if (FALSE !== ($firstUser = $firstUserQuery->fetch(PDO::FETCH_ASSOC))) {
$result=$lastMsgQuery->execute(array($thread['reply_chunk_id']));
$lastMsg = $lastMsgQuery->fetch(PDO::FETCH_ASSOC);
echo '<p>'.$lastMsg['message'].'</p>';
}
}
echo ' ' . $lastMsgQuery->rowCount();
} catch (PDOException $exc) {
...
}
Lastly, a single SQL statement can replace much of the PHP code:
SELECT mchunk.reply_chunk_id,
muser.user, MIN(muser.`timestamp`) AS grp_timestamp,
mmax.message, mmax.`timestamp` AS max
FROM messages AS mchunk
JOIN messages AS muser
ON mchunk.reply_chunk_id = muser.reply_chunk_id
JOIN messages AS mmax
ON mchunk.reply_chunk_id = mmax.reply_chunk_id
WHERE mmax.timestamp=(SELECT MAX(timestamp) FROM messages AS m WHERE m.reply_chunk_id=mchunk.reply_chunk_id)
GROUP BY mchunk.reply_chunk_id, muser.user
HAVING count(mchunk.reply_chunk_id) > 1
AND muser.user IN ('steve', '0010')
;
This selects all threads started by a specified user that have responses, along with the most recent response.