how can i pin a new in my site? - php

in my php news script
i use this code for show title of news :
<?php
$sql = mysql_query("SELECT newsid,title FROM news WHERE cat='1' ORDER BY newsid DESC LIMIT 15");
?>
and show 10 news at database order by send date
but i want pin 3 news to show first :
for example:
newstitle3 (pined)
newstitle4 (pined)
newstitle6 (pined)
newstitle1
newstitle2
newstitle5
newstitle7
....
you see 3,4 and 6 are pinned news then show unpinned news
how can i add code in my database query for this news display?

Lets think one step ahead -
Taking into consideration that there might be other areas on your site that require this pinned items behavior - I recomend a slightly more complex yet robust solution.
A more generic way of settings "pinned" items would be to create a new "pinned_items" table in your database. You mention only news items but perhaps there are other areas in your site that will also (perhaps in the future) require this "pinned" behavior.
You could have a pinned_items table with this structure -
+----+----------+--------+
| id | obj_type | obj_id |
+----+----------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 4 |
| 5 | 2 | 5 |
| 6 | 2 | 6 |
+----+----------+--------+
The obj_type would reference the type of the item (lets consider type 1 to be a news item) and the obj_id would be a reference to your news table.
So to retrieve all the "pinned" news items, you would do a query similar to this -
SELECT newsItem.* FROM news AS newsItem
LEFT JOIN `pinned_items` AS pinnedItem ON pinnedItem.obj_id = newsItem.id
WHERE pinnedItem.obj_type = 1

An easy way would be to add a 'pinned' field to your table, and then order by pinned, newsid.

Related

Relational database with multiple preferences and categories

I'm trying to wrap my head around designing my database which will store one or more preferences for many categories for each user. So in other words, each user can select one or more options from the Colors category, one or more options from the Shapes category, and so on.
My initial thought was to first have a User table with generic user information. Next, there would be a table to store all the different categories as so:
CATEGORY_ID | CATEGORY_VALUE
--------------------------------
1 | Colors
2 | Shapes
3 | Sizes
I'd separate each Category into it's own table (Colors for example):
OPTION_ID | OPTION_VALUE
------------------------------
1 | Red
2 | Blue
3 | Green
Finally, I would have a User Preferences table:
USER_ID | CATEGORY_ID | OPTION_ID
----------------------------------------
1 | 1 | 2
1 | 1 | 3
1 | 3 | 2
2 | 1 | 3
Am I on the right track here or is there a better/more efficient way to designing this. I will be setting up a search results page which will allow visitors to filter through these different categories.
Thanks!

Can SELECT, SELECT COUNT and cross reference tables be handled by just one query?

I have a page that displays a list of projects. With each project is displayed the following data retrieved from a mysqli database:
Title
Subtitle
Description
Part number (1 of x)
The total number of photos associated with that project
A randomly selected photo from the project
A list of tags
Projects are displayed 6 per page using a pagination system
As this is based on an old project of mine, it was originally done with sloppy code (I was just learning and did not know any better) using many queries. Three, in fact, just for items 5-7, and those were contained within a while loop that worked with the pagination system. I'm now quite aware that this is not even close to being the right way to do business.
I am familiar with INNER JOIN and the use of subqueries, but I'm concerned that I may not be able to get all of this data using just one select query for the following reasons:
Items 1-4 are easy enough with a basic SELECT query, BUT...
Item 5 needs a SELECT COUNT AND...
Item 6 needs a basic SELECT query with an ORDER by RAND LIMIT 1 to
select one random photo out of all those associated with each project
(using FilesystemIterator is out of the question, because the photos
table has a column indicating 0 if a photo is inactive and 1 if it is
active)
Item 7 is selected from a cross reference table for the tags and
projects and a table containing the tag ID and names
Given that, I'm not certain if all this can (r even should for that matter) be done with just one query or if it will need more than one query. I have read repeatedly how it is worth a swat on the nose with a newspaper to nest one or more queries inside a while loop. I've even read that multiple queries is, in general, a bad idea.
So I'm stuck. I realize this is likely to sound too general, but I don't have any code that works, just the old code that uses 4 queries to do the job, 3 of which are nested in a while loop.
Database structure below.
Projects table:
+-------------+---------+----------+---------------+------+
| project_id | title | subtitle | description | part |
|---------------------------------------------------------|
| 1 | Chevy | Engine | Modify | 1 |
| 2 | Ford | Trans | Rebuild | 1 |
| 3 | Mopar | Diff | Swap | 1 |
+-------------+---------+----------+---------------+------+
Photos table:
+----------+------------+--------+
| photo_id | project_id | active |
|--------------------------------|
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
| 5 | 2 | 1 |
| 6 | 2 | 1 |
| 7 | 3 | 1 |
| 8 | 3 | 1 |
| 9 | 3 | 1 |
+----------+------------+--------+
Tags table:
+--------+------------------+
| tag_id | tag |
|---------------------------|
| 1 | classic |
| 2 | new car |
| 3 | truck |
| 4 | performance |
| 5 | easy |
| 6 | difficult |
| 7 | hard |
| 8 | oem |
| 9 | aftermarket |
+--------+------------------+
Tag/Project cross-reference table:
+------------+-----------+
| project_id | tag_id |
|------------------------|
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
| 2 | 2 |
| 2 | 5 |
| 3 | 6 |
| 3 | 9 |
+------------+-----------+
I'm not asking for the code to be written for me, but if what I'm asking makes sense, I'd sincerely appreciate a shove in the right direction. Often times I struggle with both the PHP and MySQLi manuals online, so if there's any way to break this down, then fantastic.
Thank you all so much.
You're able to do subqueries inside your SELECT clause, like this:
SELECT
p.title, p.subtitle, p.description, p.part,
(SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos,
(SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo
FROM projects as p
Now, for the list of tags, as it returns more than one row, you can't do a subquery and you should do one query for every project. Well, in fact you can if you return all the tags in some kind of concatenation, like a comma separated list: tag1,tag2,tag3... but I don't recommend this one time that you will need to explode the column value. Do it only if you have many many projects and the performance to retrieve the list of tags for each individual project is fairly low. If you really want, you can:
SELECT
p.title, p.subtitle, p.description, p.part,
(SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos,
(SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo,
(SELECT GROUP_CONCAT(tag SEPARATOR ', ') FROM tags WHERE tag_id in (SELECT tag_id FROM tagproject WHERE project_id = p.project_id)) as tags
FROM projects as p
As you said from item 1 to 4 you already have the solution.
Add to the same query a SQL_CALC_FOUND_ROWS instead of a SELECT COUNT to solve the item 5.
For the item 6 you can use a subquery or maybe a LEFT JOIN limiting to one result.
For the latest item you can also use a subquery joining all the tags in a single result (separated by comma for instance).

Mysql Limit rows by field value

I'm working on simple application using PHP and MySQL. Up to this point we needed to display items from database in HTML table. Simple pagination was implemented as well. It looks something like this:
+----+---------------------+
| 1 | Item 1 |
+----+---------------------+
| 2 | Item 2 |
+----+---------------------+
| 3 | Item 3 |
+----+---------------------+
| 4 | Item 4 |
+----+---------------------+
....
+----+---------------------+
| 5 | Item 25 |
+----+---------------------+
Not a rocket science. Now we add new functionality so we can (optionally) group items - We really create a 'lot' of identical items. We decided to add new column in database called groupID - which can be number or NULL for items not contained in any group. On web page we must display it as one element which expands when you click on it.
+----+---------------------+
| 1 | Item 1 |
+----+---------------------+
| 2 | Item 2 |
+----+---------------------+
| 3 | Item 3 |
+----+---------------------+
| 4 | Group 1 (Expanded) |
+----+---------------------+
| Group 1 Item 1 |
+---------------------+
| Group 1 Item 2 |
+---------------------+
| Group 1 Item 3 |
+----+---------------------+
....
+----+---------------------+
| 25| Item 25 |
+----+---------------------+
As you can see Number of items on one page may vary so we must treat items in group as one item, so simple 'limit 25' not working anymore. I wonder if I can make some clever mysql query which will work this way. I rather want to avoid to create new table in database which consists groups and relation to item table, because most of the groups will have only 1 Item. I don't believe this functionality will be used a lot, but You know - client. Also this system works on production for some time so I'd rather avoid such changes. Any Idea how to make it work? Also please keep it simple as possible, because this example is simplified. Real query is already bit complicated.
I also want avoid parsing it via PHP code, because it's just dumb to query all few thousands of rows and then discard all but 25-50 elements.
As you said some group ids can be null, I thought we should fix that.
When null, we use the item id to for the group and we use a prefix to make sure our new group_id is unique.
This is my solution using subqueries (not pretty, but seems to work):
SELECT
i1.id,
i1.itemgroup1
FROM (
SELECT
items.id,
IF(ISNULL(items.group_id),
CONCAT('alone-', items.id),
CONCAT('group-', items.group_id)) as itemgroup1
FROM
items
) as i1
RIGHT JOIN (
SELECT
items.id,
IF(ISNULL(items.group_id),
CONCAT('alone-', items.id),
CONCAT('group-', items.group_id)) as itemgroup2
FROM
items
GROUP BY itemgroup2
LIMIT 2
) as i2 on i2.itemgroup2 = i1.itemgroup1
** UPDATE **
Removed
WHERE
items.group_id IS NOT NULL

Get 4 latest rows from each category, from same table

Before I explain my problem, I will quickly go over how the table structure is:
Type: MySQL
Posts/topic Table:
int int** UNIX Time Int Int
--------------------------------------------------
| id | category | postdate | topic_id | is_topic |
--------------------------------------------------
| 1 | a | 12345678 | 1 | 1 |
--------------------------------------------------
| 2 | a | 12345678 | 1 | 0 |
--------------------------------------------------
| 3 | b | 12345678 | 3 | 1 |
--------------------------------------------------
| 4 | b | 12345678 | 3 | 0 |
--------------------------------------------------
| 5 | c | 12345678 | 5 | 1 |
--------------------------------------------------
| 6 | c | 12345678 | 5 | 0 |
--------------------------------------------------
**I'm using letters to make is easier to read the table
I am trying to retrieve the 4 newest rows for each category, and I am able to get the 1 newest from each category with GROUP BY, but I have no idea how to get multiple for each category.
I have tried something like this:
SELECT *
FROM posts p
WHERE NOT EXISTS
(
SELECT *
FROM posts
WHERE category = p.category
LIMIT 4
)
I also tried working with some of the other answers people supplied for some other answers here on SO, but I did not seem to be able to make them fit my purpose.
I am completely lost here, as mysql is not a strong side of mine when it comes to these more complex queries.
Any help or pointers in the right directions would really be appreciated!
Thanks!
UPDATE: Please note that the number of categories is not be static, and will change.
Something like this would probably do :
select * from
(select
#rank:=CASE WHEN #ranked <> category THEN 1 ELSE #rank+1 END as rank,
id,
category,
postdate,
topic_id,
is_topic
#ranked:=category
from
(select #rank := -1) a,
(select #ranked :=- -1) b,
(select * from posts order by category, postdate desc) c
) ranked_posts
where ranked_posts.rank <= 4
Basically what is happening here is that I am trying to create a "ranking" function present in other engines (MS SQL comes in mind).
The query goes through all the posts, ordered by category, postdate and adds a "ranking" number to every row resetting the rank when the category changes. Like:
rank | category
1 a
2 a
...
100 a
1 b
2 b
1 c
2 c
3 c
You do that inside a "sub query" to mimic a table then just select the rows that have rank <= 4, (the ones you need). If you need more or less you can adjust that number.
One thing to keep in mind is that the ordering is important or the ranks will get all screwed up. The categories have to be "grouped", hence the ORDER BY category and then the groups ordered by your criteria postdate desc.

Database driven PHP navigation

Lets say we have following tables
Table Pages:
id | short_name | long_name | token
1 | Mail | My mail box | mail
2 | All mails | All mails | all
3 | Inbox | Inbox only | inb
4 | Users | Users | users
5 | All users | All users | all
and table navigation:
id | parent_id | page_id
1 | 0 | 4
2 | 0 | 1
3 | 1 | 2
4 | 1 | 3
5 | 4 | 5
I was working with only page ids for a long time. It was easy to find details of page with only 1 value - $_GET['id'], because ids of pages all are unique.
Now, I want to create human readable (token based) navigation system.
But there is 1 problem. Tokens are not always unique.
For ex. index.php?page=mail&subpage=all and index.php?page=users&subpage=all
Can't figure out, how to find short_name and long_name (or other information of page) for these 2 pages (by 2 - $_GET['page'] and $_GET['subpage'] or more variables)?
Maybe I'm in wrong way. If you think so, please suggest your idea, and explain. Thx in advance.
Sorry if this doesn't work out of the box, but does this help?
SELECT * FROM Pages
JOIN navigation ON Pages.id=navigation.page_id
WHERE navigation.parent_id=(SELECT id FROM Pages WHERE token={$page})
AND Pages.token={$subpage}

Categories