Tagging hierarchy and search - php

I am trying to create a relatively simple hierarchical tagging system that can be searched. Here's how it works as of now, this is the MySQL table structure:
--------------------------------------------
id | tag | parentID | topParentID |
--------------------------------------------
1 | Boston | NULL | NULL |
--------------------------------------------
2 | Events | 1 | 1 |
--------------------------------------------
3 | June 30th | 2 | 1 |
--------------------------------------------
4 | NYC | NULL | NULL |
--------------------------------------------
5 | Attractions | 4 | 4 |
--------------------------------------------
So, if a user types Boston in the search bar, they will be delivered the suggestions "Boston Events" and "Boston Events June 30th". Similarly, if they type NYC in the search bar, they will be delivered "NYC Attractions" as a suggestion.
Also, if someone typed Events into the search bar, they would get the suggestion "Boston Events" or if they typed June 30th, they would get the suggestion "Boston Events June 30th"
I've messed around with code to do this, and I can definitely break the query string into keywords then search the tag table for each of the keywords and return matches, but I have not found the correct way to return the full tag strings in the format I mentioned above.

Well, you can join the same table twice. Suppose, we have $id - id of the current tag:
SELECT
tags.id,
tags.tag,
parent_tags.id,
parent_tags.tag,
parent2_tags.id,
parent2_tags.tag,
FROM
tags
INNER JOIN
tags AS parent_tags
ON
tags.parentID = parent_tags.id
INNER JOIN
tags AS parent2_tags
ON
tags.topParentID = parent2_tags.id
WHERE
tags.id=$id
But it will give parents and grandparents twice because of the incorrect data in your table: parent.id = parent2.id
Actually, this is a very primitive solution, allowing only 2 levels of hierarchy to be displayed in 1 request. If you want to implement any levels, read about nested sets on stack. And there is a great book: "Trees and hierarchies in SQL for smarties" by Joe Celko

I think that you may delete the topParentID column and add one called "level" (Boston would have level 0, events level 1, June 30th level 2).
So you cold order by this level column and implode the values so you would have something like what you wish.
You can do that without the level column, but I think it will be a lot more work on the php side.

Related

MySQL Join and newest lot information

I have four tables. The first describing a mix of items. The second is a linking table between the mix, and the items. The third is the item table, and the fourth holds lot information - lot number, and when that lot starts being used.
mix
mixID | mixName
----------------
1 | Foxtrot
2 | Romeo
mixLink
mixID | itemID
----------------
1 | 1
1 | 2
1 | 3
item
itemID| itemName
----------------
1 | square
2 | triangle
3 | hexagon
itemLots
itemID| lotNo | startDate
-------------------------
1 | 22/5/3| 22/07/16
2 | 03/5 | 25/07/16
2 | 04/19 | 12/08/16
3 | 15/0 | 05/08/16
Now, I need to be able to fetch the information from the database, which details all the items from a mix, as well as the most recently used lot number, something like this:
itemName | lotNo
----------------
square | 22/5/3
triangle | 04/19
hexagon | 15/0
I've tried a dozen different mixes of joins, group by's, maxes, subqueries, and havings; all to no avail. Any help would be much appreciated, I've been pulling my hair out for hours, and I feel like my fingernails are just scraping at the solution!
This will give you the result you're after and will perform pretty well if you have your indexes done properly. I'm not sure how you're meaning to reference mix as it's not apparent in your sample output but I've included it in the WHERE clause so hopefully you can understand where you would use it.
SELECT i.itemName
, (SELECT il.lotNo FROM itemLots il
WHERE il.itemID=i.itemID
ORDER BY il.startDate desc
LIMIT 1) as lotNo
FROM item i
JOIN mixLink ml ON ml.itemID=i.itemID
JOIN mix m ON m.mixID=ml.mixID
WHERE m.mixName="Foxtrot";

Grouping items in PHP database query

I think this is a fairly simple problem, though it's a little difficult to explain.
Let's start with a list of state symbols - state flags, flowers, birds, etc. The rows in my MySQL database match each symbol with the URL at which its page appears. So if field Designation = 'flower', then the URL will be arizona/flower, new-york/flower, or something like that.
However, some states have multiple symbols in various categories; e.g. two state birds, three state flowers, etc. Making it still more confusing, I haven't yet figured out if I should describe all of a state's flower symbols on one page or make a unique page for each flower symbol.
For the time being, I'm playing it by ear, as indicated by the following database data. In this example, Arizona has two flower symbols, each discussed on the same page (URL = arizona/flower).
+----------+-----------------+-------------+
| State-ID | URL | Designation |
+----------+-----------------+-------------+
| us-az | arizona/flower | flower |
| us-az | arizona/flower | flower |
| us-az | arizona/tree | tree |
| us-fl | florida/mammal | mammal |
| us-fl | florida/mammal2 | mammal |
| us-fl | florida/mammal2 | mammal |
| us-fl | florida/mammal3 | mammal |
| us-fl | florida/bird | bird |
| us-fl | florida/bird2 | bird |
+----------+-----------------+-------------+
However, Florida has FOUR official mammals. The first one (the Florida panther) is discussed # mysite/florida/mammal. The two marine mammals are discussed at mysite/florida/mammal2, and the state horse is featured # mysite/florida/mammal3.
So here's my question: How can I write a query that 1) distinguishes between single designations (e.g. Arizona's state tree) and multiple designations (e.g. Arizona's state flowers) and 2) also tells me if the multiple designations are linked to a single URL or multiple URL's?
It will take me a while to iron out all the kinks, but, for now, it would be very helpful if I had a query that listed ONLY multiple URL's. For example, it wouldn't even display Arizona's state tree. The query could serve as sort of a snapshot of my list of symbols, helping me identify all the multiple designations and which of them are linked to single URL's vs multiple URL's.
I'm working with PHP and MySQL on a Mac.
P.S. I should point out that there are additional fields, including one that gives symbols a specific value (e.g. 'Florida panther', 'manatee', 'dolphin', 'Cracker Horse').
SELECT `State-ID`, Designation, COUNT(*) all_count, COUNT(DISTINCT URL) url_count
FROM YourTable
GROUP BY `State-ID`, Designation
url_count will tell you if they're linked to the same URL or different ones. You can add HAVING url_count > 1 to restrict the results only to items that link to multiple URLs.
You can use the HAVING clause to filter on aggregate functions
SELECT State-ID, Designation, COUNT(*) AS cnt
FROM stateTable
GROUP BY State-ID,Designation
HAVING COUNT(*)>1
That will give you all State-ID,Designation combinations that have more than one row in the table. If you also group by URL you can determine if they share a URL.
Basically you should read up on grouping and aggregation in MySQL: 12.15.1 GROUP BY (Aggregate) Functions and MySQL SELECT Syntax

MySQL Database Structure for Employee Position Database

Basically, am working with a Human Resource Management application.
Positions (like trainer, deputy manager, manager, etc) are filled up based on their service.
There is a list of all employees, the one with highest service on any given date occupies the top-most position.
The number of key positions are fixed. Based on the employee's position in the list, his probable position will be displayed.
The positions are like:
(Position - Number of Posts)
Director - 1
Manager - 3
Dy. Manager - 5
Trainer - 10
Now, in the list of employees populated based on their experience, the first in the list will be the Director, next three will be Managers, following 5 employees will be Dy. Managers and next ten would be Trainers.
I want to design a database table for storing the positions and associated numbers. There may be possibility that new positions will be added and also, the number of positions may change with time. Example, if the number of Director posts are increased to two from one, it should be edited. As soon as the number of Director posts are edited, the first two employees in the list will be designated as probable Directors. The same logic holds good to all the other positions too.
Am not sure what should be the database structure for such a table. After done, I should be able to query the table using php for probable position of any given employee based on his rank in the employees list.
Example, if an employee is in rank 5, I should be able to query the table and get the probable position. In this case, rank 5 will be Dy. Manager.
Hope I have made my requirement clear. Please help me in designing a database table for this purpose.
Thank you!
Employees
=========
name | Boss
rank | 1
doj | 2010-01-01
"Select name from Employees order by rank, doj"
doj = date of joining
Is that what you mean?
rank could join to another table, where you store the readable rank title in order to generate title, name, etc
The existing employee database table consists of:
Employees
=========
name | Mark
doj | 2010-01-01
wrk | yes
"Select name from Employees where wrk=yes order by doj"
(this will remove employees in database who has left the company, with status wrk = no)
Lets say this will generate the results something like this:
Mark | 2010-01-01
Steve | 2010-02-01
Jack | 2010-04-01
Rick | 2010-09-01
David | 2010-12-01
Now, when I fetch the results and assign position based on their seniority (assuming positions manager=1, dy. manager=3, trainer=1) the result would be:
Rank | Name | Doj | Designation
1 | Mark | 2010-01-01 | Manager
2 | Steve | 2010-02-01 | Dy. Manager
3 | Jack | 2010-04-01 | Dy. Manager
4 | Rick | 2010-09-01 | Dy. Manager
5 | David | 2010-12-01 | Trainer
After some days, lets assume Jack quits the company (his wrk status will be no). So, the fetched results would look:
Rank | Name | Doj | Designation
1 | Mark | 2010-01-01 | Manager
2 | Steve | 2010-02-01 | Dy. Manager
3 | Rick | 2010-09-01 | Dy. Manager
4 | David | 2010-12-01 | Dy. Manager
As Jack quits, David gets promoted. The rank is just the serial number generated based on the number of rows fetched, auto increment.
Now, I wanted to create a table for the designation and number of positions, so that based on the rank, it fetches the designation.

Counting rows by category-filter during fetching of categories from database

I have two tables in my database
table: products table: companies
+-----------+------------+ +------+------------+
| name | company_id | | id | name |
+-----------+------------+ +------+------------+
|Product 1 | 1 | | 1 | Company 1 |
|Product 2 | 1 | | 2 | Company 2 |
|Product 3 | 3 | | 3 | Company 3 |
|Product 4 | 1 | | 4 | Company 4 |
|Product 5 | 1 | | ... |
|Product 6 | 3 | +------+------------+
|Product 7 | 2 |
|... |
+-----------+------------+
Now I have to make company-selector (filtering products by company) using HTML SELECT element with names of all companies from table companies and COUNT of products after company name in the list.
So, my goal is to get SELECT options like this:
Company 1 (4)
Company 2 (1)
Company 3 (2)
Company 4 (0)
(note: counts inside the brackets are from example above)
What have I tried so far?
I was using mysql_* functions earlier and later it was mysqli procedural model. I can do this manually with one query for companies and another one inside while block to get COUNT of elements (filtered by current company's id in the loop). Now I'm trying to work with PDO object which is something new for me and I'm not very familiar with it.
Question
Is it somehow possible to get COUNT with one query (using JOINs or something)? If not, how I can do it with query inside the loop (old way) using my $dbPDO object? Or any other way?
I've looked some examples here but nothing could adapt to fit my requirements. Maybe I missed something but working with PDO is still painful for me (and I must learn it ASAP).
Looking at my own question, I don't think it's something hard, but the worst thing, I can't find solution by myself.
At the end, I have solution in mysqli, just I don't like it so much and think there's easier way of making this task done!
I thought this question is something I need but still don't understand that query in answer.
So far I have this code and have no idea how to make it counts products:
$dbPDO = new PDO('mysql:dbname=comixdb;host=localhost', 'root', '');
$sel_cat = ''; # selector HTML
$sql = "SELECT * FROM categories ORDER BY name";
foreach ($dbPDO->query($sql) as $row) {
$sel_cat .= "<option value=\"{$row['id']}\">{$row['name']}</option>";
}
$sel_cat = "<select><option value=\"*\">All categories</option>$sel_cat</select>";
echo $sel_cat;
Hope I've clarified question enough. Any help would be appreciated.
What you need can be done in SQL:
SELECT companies.name, SUM(IF(company_id IS NULL, 0, 1)) AS products
FROM companies
LEFT JOIN products ON (companies.id = products.company_id)
GROUP BY companies.id;
The LEFT JOIN ensures that all companies get selected, and the SUM ensures that the count is correct (a simple COUNT would return 1 for companies with no products).

Creating recommendation algorithm base on user interests

I'm currently building an application that would recommend website base on their tag.
On my website when a user registers, it will fill out an interests. So this is a sample interest:
football, model trains, hockey
So this is separated by commas. So when the user clicks on register that will be saved in my database. This is the design of my database.
userID | name | interest
001 | John Doe | sports, model trains, hockey
So on the other hand, I also have users in my sites who uploads website URLs and also creates a tag related to it. So this is my database design for that:
postID | title | tags
001 | techcrunch.com | technology,softwares,startups
002 | nba.com | basketball,sports,all-star
003 | tmz.com | gossip, showbiz
So the logic for this one is that, I wanted to recommend NBA.com to user John Doe since NBA.com has a tag of sports and John Doe's interest has a sports tag.
Do you have any idea how to do that one? Just a follow up question, Is the database design correct or should I create a new table to store all the tags. Something like that (not sure though).
Your help would be greatly appreciated and rewarded! Thanks in advance! :)
I would have normalized the database so that you have tags in a separate table and relationship tables to connect with it. As such:
User table:
UserId Name
001 John Does
TagUserRelation
UserId TagId
001 001
Tag table:
TagId TagName
001 Sports
TagUrlRelation
TagId Url
001 nba.com
001 nhl.com
To increase performance I would have continued by creating indexed views with the necessary joins and implementing stored procedures to work with them.
An alternative, as mentioned, is full text search but this will be much slower and generally not considered good database design in this case.
this can be done by using full text search
refer here
You should create two separate table which hold single tags, several for each person or post.
You can create a multi-column primary key for it if you wish.
userID | interest
001 | sports
001 | model trains
001 | hockey
...
and the same way for posts:
postID | tags
003 | gossip
003 | showbiz
...
This greatly enhances your chances to write efficient SQL.
It would be much better to store the tags separately. So that you have a table for the tags and two more tables - one for the relationship between users and tags, and one for the relationship between posts and tags.
users
----------------------------------------
userId | name | password | ....
1 | John Doe | $p$fgA |
tags
--------------------
tagId | tagname
1 | basketball
2 | hockey
user_interests
----------------------------
id | user_id | tag_id
1 | 1 | 1
2 | 1 | 2
post_tags
--------------------------
id | post_id | tag_id
1 | 1 | 2
Then you use JOINs to get the required information

Categories