i'v been trying to find a way to imitate Facebook urls on my website.
Facebook url structure are the same for all object types here are some urls and their type from Facebook:
Person - https://facebook.com/personUsername/
Page - https://facebook.com/page-identifier/
App - https://facebook.com/app-identifier/
after searching their apis i noticed they have type for each object for example a person have a type of "person".
i tried to implement this without having just one table with all my objects and a type column:
i have 5 objects on my website with 5 tables.
tracks, artists, users, albums, clips
here is how i implemented it.
a user goes to https://mysite.com/track-slug/
i take the slug and query the slug on all mysql tables until i find a match. then i render the correct view.
the question: is it possible to imitate Facebook urls without having just one mysql table or doing multiple queries on each request to mysql ?
If your MySQL tables have the same columns, you could do a UNION of these:
SELECT * FROM
(SELECT name, col1, col2 FROM table1
UNION
SELECT name, col1, col2 FROM table2
UNION
SELECT name, col1, col2 FROM table3
) AS t
WHERE t.name = 'abc'
Example result:
+------+------+------+
| name | col1 | col2 |
+------+------+------+
| abc | 1 | 2 |
+------+------+------+
If you in addition need to know the table name for each item, you could add another column in each SELECT statement:
SELECT * FROM
(SELECT name, col1, col2, 'table1' AS 'table' FROM table1
UNION
SELECT name, col1, col2, 'table2' AS 'table' FROM table2
UNION
SELECT name, col1, col2, 'table3' AS 'table' FROM table3
) AS t
WHERE t.name = 'abc'
Example result:
+------+------+------+--------+
| name | col1 | col2 | table |
+------+------+------+--------+
| abc | 1 | 2 | table2 |
+------+------+------+--------+
Even if you have different columns in your tables, you can use this technique for the tables which are the same (e.g., name, plus the table column in ex. 2) and do a second query for the rest of the columns, instead of having to do up to one query per table.
here's a hint
htaccess:
RewriteRule ^(.*)$ index.php/$1 [L]
PHP:
$uri = rawurldecode(trim(parse_url(getenv('REQUEST_URI'), PHP_URL_PATH), '/'));
$params = explode('/', $uri);
$params = explode('_', $params);
$controller = array_shift($params);
$method = array_shift($params);
now go to
domain.com/track_slug
then in your index.php do
php > echo $controller
you should get track
php > echo $method
also, you will get slug
what you do with that information is up to your controller, for example. Have the Track::slug() do your database fetches and queries.
or you can do something a little more to the poing
domain.com/query/track/slug
$uri = rawurldecode(trim(parse_url(getenv('REQUEST_URI'), PHP_URL_PATH), '/'));
$params = explode('/', $uri);
$request = array_shift($params);
$controller = array_shift($params);
$method = array_shift($params);
which then your request would be query, your controller would be track, and your method would be slug.
So now you know that you want to query the track object for the slug in your databases.
Related
I have two tables with following costruction:
errortype:
| id | errortype_text |
errorreason:
| id | errorreason_text | errortype_id |
I would like to select all data from errorreason table and replace the errortype_id data with the coresponding errortype_text.
How is this possible?
if the tables have a foreign key you can do that in your model:
$this->db->select('erroreason_text');
$this->db->join('errorreason', 'errortype.id = errorreason.id');
$query = $this->db->get('errortype');
return $query->result();
If you are talking about SQL/MySQL it should be:
SELECT `errortype_text`, `errorreason_text`, `errortype_id` FROM `errorreason` JOIN `errortype` ON `errortype`.`id`=`errorreason`.`id`
This joins your tables based on entries having the same id and it is exactly the method Yan suggested.
If you are explicitely referring to PHP/Codeigniter, you have to pass this as a parameter to mysql_query to be afterwards evaluated:
$query=mysql_query("SELECT `errortype_text`, `errorreason_text`, `errortype_id` FROM `errorreason` JOIN `errortype` ON `errortype`.`id`=`errorreason`.`id`");
I am just getting started in learning how to do INNER JOINS correctly and I can't think of the best/easiest way to do this.
I am building a url shortener and I am trying to build a query that will get all long_url.destination's matching a slug "test". One slug might point to multiple long_url.destination's(URL shuffling, GEO matching, etc...). So I need the slug to get all long_url.destination's with the same short_url.slug.
Before I was running another query to get the short_id from the slug, then running another query to select all rows in long_url that had a matching short_id.
I think it might be quicker if I use an inner join, but I am unsure how to properly set it up.
I want to get all destination columns in table long_url with only the slug data in short_url without having to run a separate query to get the short_id from the slug.
Table: short_url
Columns: short_id | slug | enabled | timestamp
example: 1 test 1 1323343922
Table: long_url
Columns: long_id | short_id | destination | geo | enabled | timestamp
example: 1 1 http://www.test.com US 1 132334922
example: 2 1 http://www.test.co.uk UK 1 132334922
I got this so far:
SELECT destination, geo FROM long_url INNER JOIN short_url
ON long_url.short_id = short_url.short_id WHERE enabled = 1;
function get_long_urls($slug) {
$query = "SELECT....";
$stmt = $db->prepare($query);
$stmt->execute(array(':slug' => $slug));
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return (array) $results:
}
example $results = array(
'http://www.test.com' => 'US',
'http://www.test.co.uk' => 'UK',
);
Thanks for any help.
select long_url.destination
, long_url.geo
from long_url
inner
join short_url
on long_url.short_id = short_url.short_id
where short_url.slug = :slug
and long_url.enabled = 1
You don't need to qualify all column names like I did, because in this particular query there wasn't any ambiguity. All I really did is add a bound parameter placeholder.
SELECT destination, geo FROM long_url LEFT JOIN short_url
ON (long_url.short_id = short_url.short_id) WHERE enabled = 1
I have several tables with different numbers and types of columns, and a single column in common.
+--------+---------+------------+-------------+
| person | beardID | beardStyle | beardLength |
+--------+---------+------------+-------------+
+--------+-------------+----------------+
| person | moustacheID | moustacheStyle |
+--------+-------------+----------------+
I want to fetch all the results that match a given value of the shared column. I can do it using multiple select statements like this:
SELECT * FROM beards WHERE person = "bob"
and
SELECT * FROM moustaches WHERE person = "bob"
But this requires multiple mysql API calls, which seems inefficient. I was hoping I could use UNION ALL to get all the results in a single API call, but UNION requires that the tables have the same number and similar type of columns. I could write a SELECT statement that would manually pad the results from each table by adding columns with NULL values, but that would quickly get unmanageable for a few more tables with a few more columns.
I'm looking for a result set roughly like this:
+--------+---------+------------+-------------+-------------+----------------+
| person | beardID | beardStyle | beardLength | moustacheID | moustacheStyle |
+--------+---------+------------+-------------+-------------+----------------+
| bob | 1 | rasputin | 1 | | |
+--------+---------+------------+-------------+-------------+----------------+
| bob | 2 | samson | 12 | | |
+--------+---------+------------+-------------+-------------+----------------+
| bob | | | | 1 | fu manchu |
+--------+---------+------------+-------------+-------------+----------------+
Is there a way to achieve this that's fast and maintainable? Or am I better off running a separate query for each table?
Clarification:
I'm not looking for a cartesian product. I don't want a row for every combination of beard-and-moustache, I want a row for every beard and a row for every moustache.
So if there are 3 matching beards and 2 matching moustaches I should get 5 rows, not 6.
this should be working fine:
SELECT * FROM `beards` b LEFT OUTER JOIN `mustaches` ON (0) WHERE person = "bob"
UNION ALL
SELECT * FROM `beards` b RIGHT OUTER JOIN `mustaches` ON (0) WHERE person = "bob"
you don't have to handle the columns by yourself. the left and right outer join do this job.
unfortunately mysql doesn't have a full join. that's why you have to do it this way with a union
SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0)
UNION
SELECT * FROM `customer` b RIGHT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0)
UNION
SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) RIGHT OUTER JOIN `day` ON (0)
this is a local test i made
Join on person....
I.e.
Select
t1.(asterix), t2.(asterix)
FROM
beards t1
INNER JOIN
moustaches t2 On t2.person = t1.person
SELECT *
FROM beards
JOIN moustaches
ON moustaches.person = beards.person
WHERE person = "bob"
I had fun with this, not sure it's entirely manageable with what more you have to add, but it accomplished the goal.
create table beard (
person varchar(20)
,beardID int
,beardStyle varchar(20)
,beardLength int )
create table moustache(
person varchar(20)
,moustacheID int
,moustacheStyle varchar(20))
insert into beard
select 'bob', 1, 'rasputin', 1
union select 'bob', 2, 'samson', 12
insert into moustache
select 'bob', 1, 'fu manchu'
declare #facialhair table (
person varchar(20)
,beardID int
,beardStyle varchar(20)
,beardLength int
,moustacheID int
,moustacheStyle varchar(20))
declare #i int
declare #name varchar(20)
set #name = 'bob'
set #i = (select COUNT(*) from beard where person = #name)
+ (select COUNT(*) from moustache where person = #name)
print #i
while #i > 0
begin
insert into #facialhair (person, beardID, beardStyle, beardLength)
select person, beardID, beardStyle, beardLength
from beard
where person = #name
set #i = #i-##ROWCOUNT
insert into #facialhair (person, moustacheID, moustacheStyle)
select person, moustacheID, moustacheStyle
from moustache
where person = #name
set #i = #i-##ROWCOUNT
end
select *
from #facialhair
I think you would be better by making queries for data in each table.
One of other possibilities is to concatenate data from all columns into one big string (you could choose some sign to separete column's values), then you should be able to use union all clause to combine results from each query - but then you will have to parse each row.. And data types will be lost.
I have 2 joined tables, each one has a primary key column named id.
SELECT t1.*, t2.* from t1 join t2 on t1.fk_id=t2.id
When I run the query above, both id fields are selected (t1.id and t2.id). My question is, how can I select the correct ID while I am looping through the result set? If I select $result->id, I will get the t2.id. Is there any way that I can get the t1.id also without explicitly selecting it in the query (i.e. t1.id as t1_id?) Also, please, let us know about some of your practices when it comes to naming the primary key columns.
Thanks!
SELECT t1.id as id1, t2.id as id2, t1.*, t2.* from t1 join t2 on t1.fk_id=t2.id
You are probably using mysqli_result::fetch_assoc to return each row of your result set as an associative array. MySQL will let you have two columns with the same name in a query, but these do not map to an associative array the way you want them to—even though the associative array is doing exactly as it should.
Assume two tables, book and author, linked by the junction table book_author. In MySQL, you can run the following query, which returns two id columns:
SELECT b.*, a.*
FROM book AS b
JOIN book_author AS ba ON ba.book_id = b.id
JOIN author AS a ON a.id = ba.author_id
LIMIT 2;
+----+-----------------+----+--------------+
| id | title | id | name |
+----+-----------------+----+--------------+
| 1 | Design Patterns | 1 | Erich Gamma |
| 1 | Design Patterns | 2 | Richard Helm |
+----+-----------------+----+--------------+
If you try to map one of these rows to an associative array, you end up with a single id element in your array:
$row = $result->fetch_assoc();
print_r($row);
Array
(
[id] => 1
[title] => Design Patterns
[name] => Erich Gamma
)
The last id column in the row will overwrite any that precede it. Here’s the second row from the result set:
Array
(
[id] => 2
[title] => Design Patterns
[name] => Richard Helm
)
This is just the same as modifying the value of an element in an associative array;
$row = array();
$row['id'] = 1;
print_r($row);
Array
(
[id] => 1
)
$row['id'] = 2;
print_r($row);
Array
(
[id] => 2
)
If you give each column a unique name in your query, either by doing so in the table itself, or giving it an alias in the query, the problem is avoided:
SELECT b.id AS book_id, b.title,
a.id AS author_id, a.name
FROM book AS b
JOIN book_author AS ba ON ba.book_id = b.id
JOIN author AS a ON a.id = ba.author_id
LIMIT 2;
+---------+-----------------+-----------+--------------+
| book_id | title | author_id | name |
+---------+-----------------+-----------+--------------+
| 1 | Design Patterns | 1 | Erich Gamma |
| 1 | Design Patterns | 2 | Richard Helm |
+---------+-----------------+-----------+--------------+
$row = $result->fetch_assoc();
print_r($row);
Array
(
[book_id] => 1
[title] => Design Patterns
[author_id] => 1
[name] => Erich Gamma
)
Alternatively, you could (and almost certainly should) use prepared statements instead. Although this can get round the problem of duplicate column names, using unique column names in your queries still makes things much easier to read and debug:
$sql = 'SELECT b.*, a.* ' .
'FROM book AS b ' .
'JOIN book_author AS ba ' .
'ON ba.book_id = b.id ' .
'JOIN author AS a ' .
'ON a.id = ba.author_id';
$stmt = $mysqli->prepare($sql);
$stmt->execute();
$stmt->bind_result($book_id, $book_title, $author_id, $author_name);
while ($stmt->fetch()) {
printf("%s, %s, %s, %s\n",
$book_id,
$book_title,
$author_id,
$author_name);
}
You'll often see the primary key for table XXX named xxx_id. This keeps the name of the same "information identifier" the same everywhere: for example in another table YYY, you'll have YYY.xxx_id with a foreign key constraint to XXX.xxx_id. This makes joins easier (you don't have to specify the "on" constraint at all in many databases) and it solves the problem you're running into as well.
I'm not saying you should prefix every column name to create a faux-namespace, but in the case of "id" it is actually useful and descriptive. It is, after all, not just any kind of ID, it's a user ID, site ID, game ID, contact ID, what have you.
Any time there is an update within my music community (song comment, artist update, new song added, yadda yadda yadda), a new row is inserted in my "updates" table. The row houses the artist id involved along with other information (what type of change, time and date, etc).
My users have a "favorite artists" section where they can do just that -- mark artists as their favorites. As such, I'd like to create a new feature that shows the user the changes made to their various favorite artists.
How should I be doing this efficiently?
SELECT *
FROM table_updates
WHERE artist_id = 1
OR artist_id = 500
OR artist_id = 60032
Keep in mind, a user could have 43,000 of our artists marked as a favorite.
Thoughts?
This depends on how your database is setup. If I had my way, I'd set it up with a table like so:
Table: user_favourite_artist
user_id | artist_id
---------------------
1 | 2
1 | 8
1 | 13
2 | 2
3 | 6
6 | 20
6 | 1
6 | 3
user_id and artist_id together would be a composite primary key. Each row specifies a user, by id, and an artist they have as a favourite, by id. A query like so:
SELECT artist_id FROM user_favourite_artist WHERE user_id = 1
Would give you the artist_id's 2, 8, and 13. This is a very simple query that will scale to your expectations.
On the reverse, when an artist is updated, you'd run this query:
SELECT user_id FROM user_favourite_artist WHERE artist_id = 2
And you would get the user_id's 1 and 2. This will tell you which users to notify. This query is also simple and will scale.
Maybe you can try this:
SELECT *
FROM table_updates
WHERE artist_id IN(1, 500, 60032)
If you have the marked artists in a secondary table, I would recomend rather using a join.
Something like
SELECT *
FORM table_updates tu INNER JOIN
table_marked_by_user tmbu ON tu.artist_id = tmbu.artist_id
WHERE tmbu.user_id = $user_id
If you're on SQL Server, you can use a nested select statement:
select * from table_updates where artist_id in
(select artist_id from favorites_table where user_id = 10)
If you don't mind doing dirty reads, you can speed it up with (nolock).
select * from table_updates (nolock) where artist_id in
(select artist_id from favorites_table (nolock) where user_id = 10)