Hello stack overflow I need help with this problem.
Ok, I have a flat file database in php it records users , hobbies, fav movies and all.
Now i want to add a buddy system so they can have friends and send messages to each other in php without SQL.
Consider having another table (er, flat file?) that maintains links. "Mark" and "John" are buddies if there exists a row in this table (ff?) that links "Mark" and "John". I'd recommend using some sort of index (you know, like a primary key).
Suppose you have a users table (or flat file, whatever, it doesn't matter that much) that contains users and some data, it looks like this:
UID | Username | Hobbies
------------------------
1 | Mark | Swimming, Sailing, Skiing
2 | John | Biking, Paragliding
3 | Suzie | Flying, Skiing
And you have this other friends table (again, flat file, whatever...):
Pair ID | A | B
----------------
1 | 1 | 2
2 | 2 | 3
We've encoded in this friends table that Mark and John are friends, and that John and Suzie are friends, but with the absence of relation 1 and 3, we know that Mark and Suzie are not friends (at least according to our records).
Note that if you want to get all of John's friends, you have to find all rows in your table (or file) that have John's UID (here = 2) in either column A or column B.
Well I am afraid there's no magical answer or a magical PHP function you can call to enable this behavior.
If we are to help you at all we really need some more to work with.
If you really, for mysterious reasons, decide to stick without a SQL database - then I would probably still "tilt" towards a SQL like way of storing it. Assuming you're currently storing each user as a row in a file, with each "field" separated by some character - simply add another "field" in the file and have this containing each "id" of every user that they're friends with (id, being whatever you use for that, could be a name as long as it is unique).
As for the messages, another flat file describing the message itself, sender and recipient would probably be the way to go.
Now the real question is, why so eager to avoid using a SQL database? If it is because of having to install a database, try SQLite
Related
Visitor opens url, for example
/transport/cars/audi/a6
or
/real-estate/flats/some-city/city-district
I plan separate table for cars and real-estate (separate table for each top level category).
Based on url (php explode create array)
$array[0] - based on the value know which table to SELECT
And so on $array[1], $array[2] ...
For example, RealEstate table may look like:
IdOfAd | RealEstateType | Location1 | Location2 | TextOfAd | and so on
----------------------------------------------------------------------
1 | flat | City1 | CityDistric1 | text.. |
2 | land | City2 | CityDistric2 | text.. |
And mysql query to display ads would be like:
SELECT `TextOfAd`, `and so on...`
WHERE RealEstateType = ? AND Location1 =? AND Location2 = ?
// and possibly additional AND .. AND
LIMIT $start, $limit
Thinking about performance. Hopefully after some long time number of active ads would be high (also i plan not to delete expired ads, just change column value to 0 not to display for SELECT; but display if directly visit from search engine).
What i need to do (change database design or SELECT in some another way), if for example number of rows in table would be 100 000 or millions?
Thinking about moving expired ads to another table (for which performance is not important). For example, from search engine user goes to some url with expired ad. At first select main table, if do not find, then select in table for expired ads. Is this some kind of solution?
Two hints:
Use ENGINE=InnoDB when creating your table. InnoDB uses row-level locking, which is MUCH better for bigger tables, as this allows rows to be read much faster, even when you're updating some of them.
ADD INDEX on suitable columns. Indexing big tables can reduce search times by several orders of magnitude. They're easy to forget and a pain to debug! More than once I've been investigating a slow query, realised I forgot a suitable INDEX, added it, and had immediate results on a query that used to take 15 seconds to run.
About
I have this table in my database holding some information saved with a user id and time.
| CONTENT | USER ID | TIME |
| text | 1 | 1405085592 |
| hello | 2 | 1405085683 |
| hey | 1 | 1405086953 |
This example could be a data dump from my database, now as you can count there is "three" rows. However I only need to know how many users there have some information in my database. Therefor the result I'm really looking for is "two", because only two users have information in the database. User ID 1 is owning both "text"(1) & "hey"(3) where user ID 2 haves "hello"(2).
In short
I want to count how many users (regardless how many rows of information they have) there are inside my database.
** What I tried **
I tried to fetch every single row into an array and then using array_unique to count them together, works fine but I do not see this as a clean and best way to do this.
Then what?
I could use the array_unique and just use count to see how many rows there are, but I'm looking for something more clean. I tried to search for this, but I'm not actually sure what I should search for in term to hit something I'm looking for. After being stuck and though I wanted to learn something new, I wanted to post this problem here.
Note
I hope you guys can help me, I have tried to make it clear what I'm looking for and what I tried. If not please let me know. Sorry if some of the above contains misspelled words, incorrect grammar or is badly explained. I do not speak English daily, but I try my best.
You are looking for the DISTINCT keyword. It returns the count of unique values of a column:
SELECT COUNT(DISTINCT user_id)
FROM your_table
See example on SQL Fiddle.
This query:
SELECT DISTINCT user_id FROM table
will return just one row for every user in the table.
I'm self-taught programmer/web-guy. And I am completely new to MYSQL. This is not a question asking how-to because I can make it do what I want. Rather I am asking for advice/critiques on how to do it better with regards to the DB design and queries I used.
A little background about me
I am not a professional programmer. Programming has always been more of a hobby interest for me. My job is designing custom jewelry using a CAD program and then have my models milled out via a CNC made especially for carving jewelry models out of wax (and a few days ago we got a 3D printer that prints in wax suitable for jewelry casting...yay!).
Why I'm doing this mysql/php project
For about a year, I've used a physical bulletin board by my workstation to track my 20 to 30 some odd design projects I always seem to have in queue. I have been wanting to make a website whereby I could enter some details about the job ticket and track it that way. This has various benefits, not the least of which is getting rid of the physical board which I always seem to be knocking the tickets off of. But mainly, the boss can view ticket statuses from anywhere in the world. A few months ago I finally got it roughed-in and it is even usable now, but I wanted to add a few more things to the project (namely, notes and special instructions).
So, I've got a main table that's approximately like...
ticket_id | lname | fname | job_desc | state_id |
----------|-------|-------|----------|-----------|
1 | Smith | Bob | dia ring | 1 |
----------|-------|-------|----------|-----------|
2 | Parker| Gil | pendant | 3 |
----------|-------|-------|----------|-----------|
There's other columns in the tickets table, but gives the general idea. The state_id refers to another table of states (statuses), which looks like so...
state_id | state_desc |
---------|----------------|
1 | Not Started |
---------|----------------|
2 | Being Designed |
---------|----------------|
3 | Rendered |
There's more states a ticket can have, but that gives the idea. The state ids end up being column heads which I use on the physical bulletin board.
I would like to have special instructions and notes for each ticket. This was something that has been awkward, though not impossible, on the physical board. It will be much nicer on the website version.
My first thought was to add another column to the tickets table like so....
ticket_id | lname | fname | job_desc | state_id | sp_instr | notes |
----------|-------|-------|----------|----------|----------|-------|
1 | Smith | Bob | dia ring | 1 | | |
----------|-------|-------|----------|----------|----------|-------|
For each ticket, there will be multiple instructions/notes and I want them to end up as unordered lists in the web page. So I would have to do something like...
Finger Size: 6.5|||Use three largest diamonds only|||Use customer's gold
And then explode on the "|||" (or some other arbitrary character combination) to get the array to make the list from. Simple enough. It was my first direction. Not too bad for the instructions. Never gonna have more than 5 or 6 short sentences. But the notes (which could double as an activity log, too) could be more complex and numerous.
So then I considered making a separate table like so...
ticket_id | instr |
----------|----------------------------------|
12 | Finger Size: 6.5 |
----------|----------------------------------|
12 | Use three largest diamonds only |
----------|----------------------------------|
12 | Use customer's gold |
----------|----------------------------------|
18 | Put bird on pendant somehow |
----------|----------------------------------|
18 | Use cust's white gold in pendant |
----------|----------------------------------|
Below is the meat of the php code that uses the database (connection info is not shown and table names have been changed)....
$db = new mysqli("{connection info}");
// put column heads in an array
$result = $db->query("SELECT * FROM states_table");
$col_heads = array();
while($row = $result->fetch_assoc()){
$col_heads[] = $row;
}
// put ticket info in an array
$result = $db->query("SELECT * FROM main_table ORDER BY due_date");
$tickets = array();
while($row = $result->fetch_assoc()){
$tickets[]=$row;
}
// do a bunch of stuff here to make HTML for the columns & tickets
// collect special instructions
$result = $db->query("SELECT main_table.ticket_id, instructions.instr FROM main_table LEFT JOIN instructions ON main_table.ticket_id = instructions.ticket_id");
while($row = $result->fetch_object()) {
var_dump($row);
}
But it's that last query that is giving me a headache. For tickets that have no special instructions, I get...
object(stdClass)#4 (2) {
["ticket_id"]=>
string(1) "6"
["instr"]=>
NULL
}
But when they do, I get a separate object for each record in the special instructions table like this...
object(stdClass)#2 (2) {
["ticket_id"]=>
string(2) "39"
["instr"]=>
string(22) "Use dias marked in red"
}
object(stdClass)#4 (2) {
["ticket_id"]=>
string(2) "39"
["instr"]=>
string(32) "Cust likes side shank of RG ring"
}
object(stdClass)#2 (2) {
["ticket_id"]=>
string(2) "39"
["instr"]=>
string(73) "Cust likes top of WG ring as well as the top of the ring from our website"
}
I am definitely able to (and did) go through that mess with a while loop and collect the instructions for a single ticket id, pack them into an array and associate them with the id so I have key=>value pairs like so...
ticket_id => array of instructions
So I can strong-arm my way through it, but I feel like I am overlooking some power in the MYSQL query statements somehow. Are my database tables and queries laughable? Have I overlooked something common/useful/powerful? Or is that just how linking a single record from one table to another table with multiple records is?
Sorry for the rambling post.
Because your question is more about MySQL than anything else, i'd say add a query we can comment on. I'll just assume what kind of advise you need here..
As for DB normalisation.. your setup looks good. Some common MySQL advice bulletpoints you might not be aware of are
Always give each table an unique (Auto-Increment) ID column, so add that to the notes table.
Consider adding field prefixes derived from the table name. This makes multi-table queries more readable. Often this is truncated to 3 or 4 characters like noteTicketID inside notes, and tickID inside the tickets table.
Try to make table names the plural of what they contain, but dont make the field names plural unless 1 field actually contains multiple values.
Do not use queries without a ORDER BY clause.
As for DB queries.. a query can be simply:
SELECT * FROM `notes` WHERE `noteTicketID`=3 ORDER BY `noteCreated` DESC
But perhaps you want to integrate several queries into one, and make a huge overview for open tickets. MySQL allows you to do this: (list every tickets of a certain state, including notes)
SELECT * FROM `tickets` WHERE `tickStateID`=0 OR `tickStateID`=1
LEFT JOIN `notes` ON `notes.noteTicketID`=`tickets.tickID`
Its certainly not "good design" to make arrays from MySQL results which you use to run further queries. If you can let MySQL do it for you in one swoop, thats a win-win situation.
As for solving the company problem, consider defining it and looking for existing solutions. Perhaps you need enterprise solutions like ERP, CRM, SCM, or just a simple issuetracker like MantisBT. This might not require much mcGyvering, but you'll likely move towards such a solution anyway eventually. The mcGyvering is certainly worth the effort though, if that helps defining the problem, but it takes a lot of time. None the less, this is how we all learned programming.. + feedback like this SO.
Conclusion:
Not your question, but install MantisBT and see how far this gets you. At the worst, it helps define the problem. But you may see a few workflow tricks that can use in a custom solution, or check out its intricate database design. And best of all, its also written in PHP and uses MySQL.
If I had to do over again, I would have called this question: MYSQL: Query that Groups Rows from Left Joined Tables???
I knew so little about MYSQL I could hardly think how to ask the question. My main issue was that the left joined table had multiple records per ticket id. I needed a way to group this data.
Enter GROUP_CONCAT.
The query that worked for me?
SELECT c.*,
GROUP_CONCAT(i.instr SEPARATOR '|||') AS instructions,
GROUP_CONCAT(n.note SEPARATOR '|||') AS notes
FROM custom_tickets c
LEFT JOIN ct_instr i ON c.ticket_id = i.ticket_id
LEFT JOIN ct_notes n ON c.ticket_id = n.ticket_id
GROUP BY c.ticket_id
The above query as used in my php code, creates an array with key => value pairs that look something like...
ticket_id => "1"
lname => "Smith"
fname => "Bob"
job_desc => "a cool ring"
instructions => "finger size: 6|||use cust's gold|||don't screw it up"
notes => "Use more diamonds now|||Make it look less stupid"
Some cool things that I (think I) learned
how to reference table names via variables within the query statement
that you can reference a joined table prior to using the JOIN keyword
(apparently) the results of GROUP_CONCAT should be treated as a column
GROUP_CONCAT is a great place to use the AS keyword. It makes for an ugly key in the array if you don't.
Also, by default, GROUP_CONCAT uses the comma as the separator. But as my notes and instructions will commonly include commas, I needed to specify a separator. I randomly picked a group of characters I am unlikely to ever use ("|||").
Thanks for every one's help.
I've recently been working on normalizing and restructuring my database to make it more effective in the long run. Currently I have around 500 records, and obviously I don't want to lose the users data.
I assume SQL through phpmyadmin is the easiest way to do this?
So let me give you guys an example
In my old table I would have something like this
records //this table has misc fields, but they are unimportant right now
id | unit |
1 | g |
With my new one, I have it split apart 3 different tables.
records
id
1
units
id | unit
1 | g
record_units
id | record_id | unit_id
1 | 1 | 1
Just to be clear, I am not adding anything into the units table. The table is there as a reference for which id to store in the record_units table
As you can see it's pretty simple. What moved in the second table is that I started using an index table to hold my units, since they would be repeated quite often. I then store that unit id, and the pairing record id in the record_units table, so I can later retrieve the fields.
I am not incredibly experienced with SQL, though i'd say I know average. I know this operation would be quite simple to do with my cakephp setup, because all my associations are already setup, but I can't do that.
If I understand correctly you want to copy related records from your old table to the new tables, in which case you can use something like this
UPDATE units u
INNER JOIN records r
ON u.id=r.id
SET u.unit = r.unit
This will copy the unit type from your old table to the matching id in the new units table and then you can do something similare on your 3rd table
I have a table which would contain information about a certain month, and one column in that row would have mysql row id's for another table in it to grab multiple information from
is there a more efficent way to get the information than exploding the ids and doing seperate sql queryies on each... here is an example:
Row ID | Name | Other Sources
1 Test 1,2,7
the Other Sources has the id's of the rows from the other table which are like so
Row ID | Name | Information | Link
1 John | No info yet? | http://blah.com
2 Liam | No info yet? | http://blah.com
7 Steve| No info yet? | http://blah.com
and overall the information returned wold be like the below
Hi this page is called test... here is a list of our sources
- John (No info yet?) find it here at http://blah.com
- Liam (No info yet?) find it here at http://blah.com
- Steve (No info yet?) find it here at http://blah.com
i would do this... i would explode the other sources by , and then do a seperate SQL query for each, i am sure there could be a better way?
Looks like a classic many-to-many relationship. You have pages and sources - each page can have many sources and each source could be the source for many pages?
Fortunately this is very much a solved problem in relational database design. You would use a 3rd table to relate the two together:
Pages (PageID, Name)
Sources (SourceID, Name, Information, Link)
PageSources (PageID, SourceID)
The key for the "PageSources" table would be both PageID and SourceID.
Then, To get all the sources for a page for example, you would use this SQL:
SELECT s.*
FROM Sources s INNER JOIN PageSources ps ON s.SourceID = ps.SourceID
AND ps.PageID = 1;
Not easily with your table structure. If you had another table like:
ID Source
1 1
1 2
1 7
Then join is your friend. With things the way they are, you'll have to do some nasty splitting on comma-separated values in the "Other Sources" field.
Maybe I'm missing something obvious (been known to), but why are you using a single field in your first table with a comma-delimited set of values rather than a simple join table. The solution if do that is trivial.
The problem with these tables is that having a multi-valued column doesn't work well with SQL. Tables in this format are considered to be normalized, as multi-valued columns are forbidden in First Normal Form and above.
First Normal Form means...
There's no top-to-bottom ordering to the rows.
There's no left-to-right ordering to the columns.
There are no duplicate rows.
Every row-and-column intersection contains exactly one
value from the applicable domain (and
nothing else).
All columns are regular [i.e. rows have no hidden components such as
row IDs, object IDs, or hidden timestamps].
—Chris Date, "What First Normal Form Really Means", pp. 127-8[4]
Anyway, the best way to do it is to have a many to many relationship. This is done by putting a third table in the middle, like Dominic Rodger does in his answer.