searching mySQL by first char(s) in fields with php - php

I have a database that's setup in the following way
id coach team event status
14 test 8,7,12,13,15 4,1,2,14,4 2
13 test 8,12,13,14,15,16 1,2,8,16,16 3
What i need to do, is search for rows where the first number in the "event" column matches the query. They are separated by commas, but it can be 1 or 2 or 3 digits. Im sure this is possible, just not sure how or where to begin.

Have you considered normalizing your database? Isn't it a pain to work with a database, in which a field may contain an arbitrary number of arbitrarily formatted values? As a side effect (haha), it will solve the problem you've described in your question.
Example database schema:
create table whatever (
id int not null auto_increment primary key,
coach varchar(64),
status int
)
create table teams (
id int not null auto_increment primary key,
name varchar(255)
)
create table events (
id int not null auto_increment primary key,
name varchar(255)
)
create table whatever_teams (
id int not null auto_increment primary key,
whatever_id int,
team_id int
)
create table whatever_events (
id int not null auto_increment primary key,
whatever_id int,
event_id int
)
I want to apologize in advance for the obvious lack of sql-injection-enabled code, that can be always found in the questions and answers under the tags "php" and "mysql".

This will select all rows where the first number in event is 1:
SELECT * FROM `tableName` WHERE event LIKE '1,%';

You'd be better of by changing your database scheme. Storing fields with lists of ids is not very handy.
Make extra tabels to make the links.
For example:
coach_team
id coach_id team_id
1 14 7
2 14 8
3 14 12
4 14 13
Than you can use queries like:
SELECT * FROM table_name WHERE id in
(SELECT coach_id FROM coach_team WHERE team_id = 1)
(This of course also applies to events.
Extra information:
http://en.wikipedia.org/wiki/Database_normalization

You could use SUBSTRING_INDEX to get the first value, something like this:
SELECT * FROM table_name WHERE SUBSTRING_INDEX( event, ',', 1 ) = 'value'
With this approach you can use a prepared statement with a placeholder for the search value. Also works fine if there is just one number in the event column, i.e. no commas present to match against.

$sql = "SELECT * FROM table_name WHERE event LIKE '" . $query . "',%'";

I strongly recommend you to change your database schema because from my experience, sooner or later, you have to change it to serve all your needs in the future. SHould do it now be4 too late

Related

php MySql 2 column as index and 1 as another index?

I'm using php and i have a table that have 2 column of varchar , one is used for user identification, and the other is used for page name entry.
they both must be varchar.
i want to insert ignore data when user enter a page to know if he visited it or not, and i want to fetch all the rows that the user have been in.
fetch all for first varchar column.
insert if not exist for both values.
I'm hoping to do it in the most efficient way.
what is the best way to insert without checking with another query if exist?
what is the best way other then:
SELECT * FROM table WHERE id = id
to fetch when the column needed is varchar?
You should consider a normalized table structure like this:
CREATE TABLE user (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE page (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE pages_visted (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED,
page_id INT UNSIGNED,
UNIQUE KEY (user_id, page_id)
);
INSERT IGNORE INTO pages_visted (user_id, page_id) VALUES (:userId, :pageId);
SELECT page_id FROM pages_visted WHERE user_id = :userId;
I think you want to implement a composite primary key.
A composite primary key tells MySQL that you want your primary key to be a combination of fields.
More info here: Why use multiple columns as primary keys (composite primary key)
I don't know of a better option for your query, although I can advise, if possible:
Define columns to be NOT NULL. This gives you faster processing and requires less storage. It will also simplify queries sometimes because you don't need to check for NULL as a special case.
And with variable-length rows, you get more fragmentation in tables where you perform many deletes or updates due to the differing sizes of the records. You'll need to run OPTIMIZE TABLE periodically to maintain performance.

Mysql Auto-incrementing int of similar data

So this is more of a query of how one might go about doing this, I'm new to MySQL/PHP coding when it comes to more than the basics so I'm just wondering how one might set up an auto incrementing int where if two lastnames were the same it would count them.
I was unable to find anything on it while searching online but an example would be:
in the database we have 5 users
1. james smith 1
2. terry smith 2
3. john smith 3
4. jerry fields 1
5. tom straus 1
When these users register I need an int to be created that john smith was the 3rd person to have the same last name of smith while jerry fields is the first person with the last name fields etc. How might one do that?
The form I made is one that registers a user using a jquery/php ajax method but
I would like to add something similar to this so that it combines that number with their names to make a specific user ID.
As documented under Using AUTO_INCREMENT:
For MyISAM and BDB tables you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups.
Therefore, you could do:
CREATE TABLE my_table (
firstname VARCHAR(31) NOT NULL,
lastname VARCHAR(31) NOT NULL,
counter BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (lastname, counter)
) Engine=MyISAM;
INSERT INTO my_table
(firstname, lastname)
VALUES
('james', 'smith' ),
('terry', 'smith' ),
('john' , 'smith' ),
('jerry', 'fields'),
('tom' , 'straus')
;
See it on sqlfiddle.

Sqlite 3 Insert and Replace fails on more than 1 unique column

I am using a table to store votes of different users on different polls.
Table has following structure.
id | poll_id | opt | ip_address
id : auto increment
poll_id : (STRING UNIQUE) unique for a particular poll
opt : (STRING) option selected by user
ip_address: (STRING UNIQUE) ip address of user
This is my query
INSERT OR REPLACE INTO tbl_poll(id,poll_id,opt,ip_addr) VALUES (null,'$poll_id','$opt','$ip_addr')
(Here $poll_id, $opt and $ip_addr are php variables which holds the respective values)
Now, the scenario is like this,
User 'A' votes for option 2 of poll_id 'mypoll'. Query works perfectly. (Does insert)
User 'A' changes mind and votes for option 5 of poll_id 'mypoll'. Query works perfectly. (Does replace)
But if User 'A' votes for option 4 of poll_id 'yourpoll'. Query fails (It does a replace) but it should insert a new record with poll_id 'yourpoll'
I think, it considers the unqiue constraint of ip_address only but not the poll_id
From your description on the desired functionality, it would seem that you want poll_id and ip_address to be a unique pair or compound unique.
CREATE TABLE tbl_poll (
id INTEGER PRIMARY KEY AUTOINCREMENT,
poll_id STRING NOT NULL,
ip_address STRING NOT NULL,
opt STRING NULL,
CONSTRAINT 'unique_vote_per_poll_per_ip_address' UNIQUE ( poll_id, ip_address ) ON CONFLICT REPLACE
);
No, this is correct behaviour.
You have a UNIQUE constraint on ip_address, therefore you may not have two records with the same ip_address.
Put your UNIQUEconstraint on the pair (poll_id, ip_address) instead.

Getting the total for two queries in PHP

I'm tracking costs to clients by session and by items specific to each session. I'm trying to get the total session costs and session item costs (cost * count from tbl_sessionitem). But when I check the results, the code outputs the error:
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource
Here are my tables:
CREATE TABLE tbl_session (
`clientid` INT UNSIGNED NOT NULL,
`sessioncost` DECIMAL(6,2) NOT NULL,
`datetoday` DATETIME NOT NULL,
);
CREATE TABLE tbl_sessionitem (
`clientid` INT UNSIGNED NOT NULL,
`cost` DECIMAL(6,2) NOT NULL,
`count` INT UNSIGNED NOT NULL,
`datetoday` DATETIME NOT NULL
);
Here is my php code:
<?php
$date=$_POST['date'];
mysql_connect("localhost","root","");
mysql_select_db("database");
$sql=mysql_query("
SELECT id
, SUM(tbl_session.sessioncost) AS 'totalcost'
, SUM(tbl_sessionitem.count) * SUM(tbl_sessionitem.cost) AS 'totalquantitycost'
FROM (
SELECT clientid
, sessioncost
FROM tbl_session
WHERE datetoday = ('$date')
UNION ALL
SELECT clientid
, cost
, count
FROM tbl_sessionitem
WHERE datetoday = ('$date')
)
GROUP BY id");
while($row = mysql_fetch_array($sql))
{
echo $row['totalcost'];
echo $row['totalquantitycost'];
}
mysql_close();
?>
The warning means what it said: the value passed to mysql_fetch _array isn't a result. mysql_query returns a mixed value; when the query fails, it returns false. You need to perform error checking. mysql_error will give you an error message from MySQL, though be careful never to output database error messages to non-admins.
If you had done that, you would have seen a number of problems:
the subselect result must be given an alias.
the selects being UNIONed have a different number of columns
there's no column named "id" in the subselect results.
the aggregate functions reference the tables from the subselect, but the outer select can only access the result table (the one missing an alias).
Even if you fix those SQL errors, the query itself won't give the results you're looking for, due to the way grouping and aggregate functions work.
There's a much better approach. Session items are associated with sessions, but in the schema this association is loose, via the datetoday column. As a result, you have the odd use of unions. Instead, create surrogate keys for the tables and give the session items table a column that refers to the session table. While you're at it, drop the redundant "tbl_" prefix.
CREATE TABLE sessions (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
client INT UNSIGNED NOT NULL,
cost DECIMAL(5,2),
`date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
FOREIN KEY (client) REFERENCES clients (id)
) Engine=InnoDB;
CREATE TABLE session_items (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
session INT UNSIGNED NOT NULL,
cost DECIMAL(5,2),
`count` INT UNSIGNED,
FOREIN KEY (session) REFERENCES sessions (id)
) Engine=InnoDB;
To get the total session cost and quantity cost for a given day, you can use a subquery to get the quantity cost for a session (necessary to prevent including session costs multiple time in the totalcost sum), then sum the session and quantity costs in an outer query for each client's total costs for a given day.
SELECT client,
SUM(cost) AS totalcost,
SUM(quantitycost) AS totalquantitycost
FROM (
SELECT client,
sessions.cost,
SUM(session_items.`count`) * SUM(session_items.cost) AS quantitycost
FROM sessions
JOIN session_items ON sessions.id=session_items.session
WHERE sessions.`date` = NOW()
GROUP BY sessions.id
) AS session_invoices
GROUP BY client
;
COUNT is not to be used as a Column name, it's a function, it's used like this:
Select COUNT(id) as countOfId FROM table
Also, I would recommend doing all those calculations in PHP, much easier to maintain and probably better performance, MySql isn't meant as a calculator.
If you want to use reserved keywords as column names, you need to add backticks and don't write them in capitals because that decreases readability in this case:
Select `count` from table
And what is COST?

Change Column to Auto_Increment

I asked this question a little earlier today but am not sure as to how clear I was.
I have a MySQL column filled with ordered numbers 1-56. These numbers were generated by my PHP script, not by auto_increment.
What I'd like to do is make this column auto_incrementing after the PHP script sets the proper numbers. The PHP script works hand in hand with a jQuery interface that allows me to reorder a list of items using jQuery's UI plugin.
Once I decide what order I'd like the entries in, I'd like for the column to be set to auto increment, such that if i were to insert a new entry, it would recognize the highest number already existing in the column and set its own id number to be one higher than what's already existing.
Does anyone have any suggestions on how to approach this scenario?
I'd suggest creating the table with your auto_increment already in place. You can specify a value for the auto_inc column, and mysql will use it, and still the next insert to specify a NULL or 0 value for the auto_inc column will magically get $highest + 1 assigned to it.
example:
mysql> create table foobar (i int auto_increment primary key);
mysql> insert into foobar values (10),(25);
mysql> insert into foobar values (null);
mysql> select * from foobar;
# returns 10,25,26
You can switch it to MySQL's auto_increment implementation, but it'll take 3 queries to do it:
a) ALTER TABLE to add the auto_increment to the field in question
b) SELECT MAX(id) + 1 to find out what you need to set the ID to
c) ALTER TABLE table AUTO_INCREMENT =result from (b)
MySQL considers altering the AUTO_INCREMENT value a table-level action, so you can't do it in (a), and it doesn't allow you to do MAX(id) in (c), so 3 queries.
You can change that with a query, issued through php, using the mysql console interface or (easiest) using phpmyadmin.
ALTER TABLE table_name CHANGE old_column_name new_column_name column_definition;
ALTER TABLE table_name AUTO_INCREMENT = highest_current_index + 1
column_definiton:
old_column_definition AUTO_INCREMENT
More info:
http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
http://dev.mysql.com/doc/refman/5.1/en/create-table.html
EDIT
Always use mysql_insert_id or the appropiate function of your abstraction layer to get the last created id, as LAST_INSERT_ID may lead to wrong results.
No, stop it. This isn't the point of auto_increment. If you aren't going to make them ordered by the id then don't make them auto_increment, just add a column onto the end of the table for ordering and enjoy the added flexibility it gives you. It seems like you're trying to pack two different sets of information into one column and it's really only going to bite you in the ass despite all the well-meaning people in this thread telling you how to go about shooting yourself in the foot.
In MySQL you can set a custom value for an auto_increment field. MySQL will then use the highest auto_increment column value for new rows, essentially MAX(id)+1. This means you can effectively reserve a range of IDs for custom use. For instance:
CREATE TABLE mytable (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
col1 VARCHAR(256)
);
ALTER TABLE mytable AUTO_INCREMENT = 5001;
In this schema all ids < 5001 are reserved for use by your system. So, your PHP script can auto-generate values:
for ($i=1; $i<=56; $i++)
mysql_query("INSERT INTO mytable SET id = $i, col1= 'whatevers'");
New entries will use the non-reserved range by not specifying id or setting it to null:
INSERT INTO mytable SET id = NULL, col1 = 'whatevers2';
-- The id of the new row will be 5001
Reserving a range like this is key - in case you need more than 56 special/system rows in the future.
ALTER TABLE <table name> <column name> NOT NULL AUTO_INCREMENT
More info:
AUTO_INCREMENT Handling in InnoDB
Server SQL Modes

Categories