select query + join tables - php

I have a table called indx_0 where I select all "pid" (product id) cominations with "wid" (word id) grouping them by products that match the most words. Now, since the actual words are stored within a different table called "windex" the table indx_0 only contains product ID matched with the ID of the word.
Here is the current query I use to get the results.
SELECT pid, count(*) WordMatchCount
FROM indx_0
WHERE wid in ( 294, 20591, 330 )
group by pid
order by WordMatchCount desc
limit 1000
Say I search for "ddr memory card" I will NOT get the result that contains "ddr3" prioritized over any other keyword since it searches for exact match. so "ddr memory card" and "phone memory card" would be treated as equal since neither "ddr3" nor "phone" equals to ddr.
I want to use join and LIKE (or any other preferable way) to allow "ddr" match against "ddr3" or "ddr2" very closely to prioritize it over other results where there is no close match.
Here are table structures:
CREATE TABLE IF NOT EXISTS `windex` (
`word` varchar(64) NOT NULL,
`wid` int(10) NOT NULL AUTO_INCREMENT
PRIMARY KEY (`wid`),
UNIQUE KEY `word` (`word`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=834922 ;
CREATE TABLE IF NOT EXISTS `indx_0` (
`wid` int(7) NOT NULL,
`pid` int(7) NOT NULL,
UNIQUE KEY `wid` (`wid`,`pid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Is this possible?
thank you!

If you're filtering by word id, you're obviously not getting close matches. Every ID has a specific word. What you want is to query the IDs directly from windex and looking for the matches in indx_0.
SELECT i0.pid, count(*) WordMatchCount
FROM indx_0 AS i0
LEFT JOIN windex AS wi ON (wi.wid = i0.wid)
WHERE wi.word REGEXP '(.*)ddr(.*)|(.*)memory(.*)|(.*)card(.*)'
group by i0.pid
order by WordMatchCount desc
I'd make sure to run EXPLAIN and index the appropriate columns, though.

Related

How to use concat the right way in my mySQL to get the percentages of which tools have been registered the most

I would like to have some statistics and calculate the percentages of which tools have been chosen the most overall in all the registrations of my database
These are my two tables:
$table_registration = $wpdb->prefix . 'registration';
$table_tools = $wpdb->prefix . 'tools';
wp_registration table:
CREATE TABLE $table_registration
(
reg_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
dato date,
billedeURL VARCHAR(80) NOT NULL,
fiske_vaegt DECIMAL( 2,1 ) NOT NULL,
fiske_laengde INT NOT NULL,
reg_user_id BIGINT UNSIGNED NOT NULL,
reg_tools_id INT UNSIGNED NOT NULL
PRIMARY KEY (reg_id),
FOREIGN KEY (reg_user_id) REFERENCES wp_users(id),
FOREIGN KEY (reg_tools_id) REFERENCES $table_tools(tools_id)
)
wp_tools table:
CREATE TABLE $table_tools
(
tools_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
tools_navn CHAR (20),
PRIMARY KEY (tools_id)
)
I have been trying to create the correct mysql but with no luck so this is what I've been doing up till now.
select l.*, concat(round(100 * count(t.reg_tools_id) / t2.cnt,0),'%')
from wp_registration l
left join wp_tools t on l.toolss_id = t.reg_id
cross join
(select count(*) cnt
from wp_registration
where reg_tools_id = 1) t2
group by l.reg_id;
But it tells me that every tool has been used 50% of the times. which obviously is wrong I have three tools users can choose from and right now have 1 - two votes and 2 - nine votes and 3 - two votes there are 13 registrations in total
Hopefully, I understand what do you need !
SELECT
tools.tools_id,
((COUNT(*) / (SELECT COUNT(*) FROM registration)) * 100) AS percent
FROM
registration
JOIN
tools ON registration.reg_tools_id = tools.tools_id
GROUP BY
tools.tools_id
ORDER BY
percent DESC
LIMIT 1
Some remarks :
Try to write in a pure sql
You do not need a php tag for this question
Minimize your code from unnecessary part
Use the concat and round functions in the programming language that you are using not in SQL (I think you are using php here, so do the query then get the result and apply the round and the concat in php instructions)

proper query in php

I had some help with this code:
SELECT *, IF(newcost - cost > 500, newcost - cost, 0) as raiseby FROM
(
SELECT bp.*, b.company,(IF(bp.cost*1.2 < ls.maximumbid, bp.cost*1.2, bp.cost)) as newcost
FROM `windows_brands_products` bp
LEFT JOIN `windows_brands` b
ON bp.brand_id = b.id
JOIN Windows_last_submissions ls
JOIN windows_materials dm
WHERE ls.username = '$current_user->user_login'
AND bp.width = ROUND(ls.width)
AND bp.height = ROUND(ls.height)
AND bp.material IN (dm.name)
AND bp.type = ls.type
AND IF (ls.minimumbid != '0.00',bp.cost BETWEEN ls.minimumbid AND ls.maximumbid,bp.cost <= ls.maximumbid)
ORDER BY b.company ASC
) as temptable
The goal is to display a line stating that the user missed a (or some) companies by $500 and state how much the user needs to increase their max. bid (ls.maximum) in order to come within parameters of the query. It looks to me like it will look something like:
echo 'If you increase your maximum bid by '.$row->raiseby.',. $row->temptable.'can perform your installation';
I don't seem to get a positive response by using that statement...My assumptions could also be off about the way to reference each "row".
Am I wrong to assume that when you use "AS" parameter in sql the word to the right becomes the new row name?
Anyone have any idea as to how I could word this query to achieve my results?
Thanks
EDIT* I'm having a problem with sqlfiddle showing an error I can't figure out. it states 'incorrect integer value: column 'id' row 1':
CREATE TABLE windows_brands
(
id int(11),
company varchar(255)
);
INSERT INTO windows_brands
VALUES ('1','Anderson');
CREATE TABLE windows_brands_products
(
id int(11) AUTO_INCREMENT,
brand_id int(11),
width decimal(12,2),
height decimal(12,2),
material varchar(30),
type varchar(30),
cost decimal(12,2),
PRIMARY KEY (id)
);
INSERT INTO windows_brands_products
VALUES ('','2','30','36','wood','double hung','1500.00');
CREATE TABLE Windows_submissions
(
id int(11) AUTO_INCREMENT,
name varchar(30),
username varchar(12),
width decimal(12,2),
height decimal(12,2),
chosenmaterial varchar(30),
type varchar(30),
minimumbid decimal(12,2),
maximumbid decimal(12,2),
PRIMARY KEY (id)
);
INSERT INTO Windows_submissions
VALUES ('','Casey','caseys','30','36','wood','double hung','1000.00','1700.00');
CREATE TABLE windows_materials
(
id int(11) AUTO_INCREMENT,
name varchar(30),
PRIMARY KEY (id)
);
INSERT INTO windows_materials
VALUES ('','wood');
Can you see what the problem is - I've been looking it over for a while and can't see it.
This query works fine: http://sqlfiddle.com/#!9/22a74/1
In the query I'm trying to get to work...If, say, I make a maximumbid of $300 (for example) more than what the company charges, I should be able to echo something in a loop like, the nearest company is ____ and is $300 more than your maximumbid. I just don't know how to reference the aliases like 'raiseby' and 'temptable'. If I put that query into sqlfiddle 0 results show...which I want the same results like the company and the newcost to show- but also I want to let the user know how much they missed the target if, say, no results show.

what's wrong with this SQL statement causing column count doesn't match value count at row 1?

Mysql table (migration_terms) fields are as follows
oldterm count newterm seed
I used the following create table statment.
CREATE TABLE `migration_terms`
(
`oldterm` varchar(255) DEFAULT NULL,
`count` smallint(6) DEFAULT '0',
`newterm` varchar(255) DEFAULT NULL,
`seed` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`seed`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
And It works, no problems there.
but then when I used the following insert into statement to populate it;
"INSERT INTO migration_terms
SELECT looseterm as oldterm,
COUNT(seed) AS count
FROM looseterms
GROUP BY looseterm
ORDER BY count DESC "
I get this error;
Column count doesn't match value count at row 1
I cannot figure out why?
If you need the table structure of the looseterms table, it was created by the following create table statement.
CREATE TABLE looseterms
(
`seed` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`looseterm` varchar(255)
)
You need to specify the columns if your select statement has fewer columns than the table
"INSERT INTO migration_terms
(oldterm,
count)
SELECT looseterm AS oldterm,
Count(seed) AS count
FROM looseterms
GROUP BY looseterm
ORDER BY count DESC "
From MySql docs on Insert Syntax
If you do not specify a list of column names for INSERT ... VALUES or
INSERT ... SELECT, values for every column in the table must be
provided by the VALUES list or the SELECT statement. If you do not
know the order of the columns in the table, use DESCRIBE tbl_name to
find out.
Your insert is adding 2 columns of data, whereas your table's definition has 4 columns

PHP/MySQL Search code and logic for relational database schema

I have created this database schema and with help from several users on here, I have a database which takes user submitted business entries stored in the business table, which are additionally grouped under one or several of about 10 catagories from the catagories table, in the tbl_works_catagories table by matching the bus_id to the catagory id.
For example, bus_id 21 could be associated with catagory_id 1, 2, 5, 7, 8.
CREATE TABLE `business` (
`bus_id` INT NOT NULL AUTO_INCREMENT,
`bus_name` VARCHAR(50) NOT NULL,
`bus_dscpn` TEXT NOT NULL,
`bus_url` VARCHAR(255) NOT NULL,
PRIMARY KEY (`bus_id`)
)
CREATE TABLE `categories` (
`category_id` INT NOT NULL AUTO_INCREMENT,
`category_name` VARCHAR(20) NOT NULL,
PRIMARY KEY (`category_id`)
)
CREATE TABLE `tbl_works_categories` (
`bus_id` INT NOT NULL,
`category_id` INT NOT NULL
)
Now, what i want to do next is a search function which will return businesses based on the catagory. For example, say one of the businesses entered into the business table is a bakers and when it was entered, it was catagorised under Food (catagory_id 1) and take-away (catagory_id 2).
So a visitor searches for businesses listed under the Food catagory, and is returned our friendly neighbourhood baker.
As with all PHP/MySQL, i just can't (initially anyway) get my head around the logic, never mind the code!
You should setup foreign keys in your tables to link them together.
CREATE TABLE `business` (
`bus_id` INT NOT NULL AUTO_INCREMENT,
`bus_name` VARCHAR(50) NOT NULL,
`bus_dscpn` TEXT NOT NULL,
`bus_url` VARCHAR(255) NOT NULL,
PRIMARY KEY (`bus_id`)
)
CREATE TABLE `categories` (
`category_id` INT NOT NULL AUTO_INCREMENT,
`category_name` VARCHAR(20) NOT NULL,
PRIMARY KEY (`category_id`)
)
CREATE TABLE `tbl_works_categories` (
`bus_id` INT NOT NULL,
`category_id` INT NOT NULL,
FOREIGN KEY (`bus_id`) REFERENCES business(`bus_id`),
FOREIGN KEY (`category_id`) REFERENCES categories(`category_id`)
)
Then your search query would be something like:
SELECT b.*
FROM business b, categories c, tbl_works_categories t
WHERE
b.bus_id = t.bus_id AND
c.category_id = t.category_id AND
c.category_id = *SOME SEARCH VALUE*
which using JOIN would be written as:
SELECT b.*
FROM business b
JOIN tbl_works_categories t
ON b.bus_id = t.bus_id
JOIN categories c
ON c.category_id = t.category_id
WHERE c.category_id = *SOME SEARCH VALUE*
Maybe you want something like this:
SELECT `bus_id` FROM `tbl_works_categories` WHERE `category_id` = *some id from the search*
AND `category_id` = *some other id from the search*;
Although you'd need those ids- there are a few ways to do this, I'll describe probably the most straight forward...
You get categories from $_POST, so let's just say you have 2 of them entered. (Food, and take-away). Parse these however you want, there are multiple ways, but the point is they're coming from $_POST.
execute this sort of thing for each one you find:
SELECT `category_id` FROM `categories` WHERE `category_name` LIKE '%*the name from $_POST*%';
Store these results in an array...based on how many you have there you can build an applicable query similar to the one I describe first. (Keep in mind you don't need and AND there, that's something you have to detect if you return > 1 category_id from the second query here)
I'm not going over things like security..always be careful when executing queries that contain user submitted data.
An alternate solution might involve a join, not too sure what that'd look like off the top of my head.
Good luck.
If you want all businesses that are related to the given category-id, your SQL-statement would look something like this:
SELECT `business`.`bus_name`
FROM `business`
WHERE `business`.`bus_id` = `tbl_works_categories`.`bus_id`
AND `categories`.`category_id` = `tbl_works_categories`.`category_id`
AND `categories`.`category_id` = 1;
Where 1 in this case is your food-category, but could be your PHP variable where the ID of the category the user selected is stored.
And one hint: Be sure to name your tables either in plurar or in singular. You are mixing both and could get confused.

MySQL Left Join, Group By, Order By, Limit = Terrible Performance

I am currently developing a an application to allow users to search through a database of documents using various paramaters and returning a set of paged results. I am building it in PHP/MySQL, which is not my usual development platform, but its been grand so far.
The problem I am having is that in order to return a full set of results I have to use LEFT JOIN on every table, which completely destroys my performance. The person who developed the database has said that the query I am using will return the correct results, so thats what I have to use. The query is below, I am by no means an SQL Guru and could use some help on this.
I have been thinking that it might be better to split the query into sub-queries? Below is my current query:
SELECT d.title, d.deposition_id, d.folio_start, d.folio_end, pl.place_id, p.surname, p.forename, p.person_type_id, pt.person_type_desc, p.age, d.manuscript_number, dt.day, dt.month, dt.year, plc.county_id, c.county_desc
FROM deposition d
LEFT JOIN person AS p ON p.deposition_id = d.deposition_id
LEFT JOIN person_type AS pt ON p.person_type_id = pt.person_type_id
LEFT JOIN place_link AS pl ON pl.deposition_id = d.deposition_id
LEFT JOIN date AS dt ON dt.deposition_id = d.deposition_id
LEFT JOIN place AS plc ON pl.place_id = plc.place_id
LEFT JOIN county AS c ON plc.county_id = c.county_id
WHERE 1 AND d.manuscript_number = '840'
GROUP BY d.deposition_id ORDER BY d.folio_start ASC
LIMIT 0, 20
Any help or guidance would be greatly appreciated!
Deposition Table:
CREATE TABLE IF NOT EXISTS `deposition` (
`deposition_id` varchar(11) NOT NULL default '',
`manuscript_number` int(10) NOT NULL default '0',
`folio_start` varchar(4) NOT NULL default '0',
`folio_end` varchar(4) default '0',
`page` int(4) default NULL,
`deposition_type_id` int(10) NOT NULL default '0',
`comments` varchar(255) default '',
`title` varchar(255) default NULL,
PRIMARY KEY (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Date Table
CREATE TABLE IF NOT EXISTS `date` (
`deposition_id` varchar(11) NOT NULL default '',
`day` int(2) default NULL,
`month` int(2) default NULL,
`year` int(4) default NULL,
PRIMARY KEY (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Person_Type
CREATE TABLE IF NOT EXISTS `person_type` (
`person_type_id` int(10) NOT NULL auto_increment,
`person_type_desc` varchar(255) NOT NULL default '',
PRIMARY KEY (`person_type_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=59 ;
Seems that you want to select one person, place etc. per deposition.
The query you wrote will return you this, but it's not guaranteed which one will it return, and the query is inefficient.
Try this:
SELECT d.title, d.deposition_id, d.folio_start, d.folio_end, pl.place_id, p.surname, p.forename, p.person_type_id, pt.person_type_desc, p.age, d.manuscript_number, dt.day, dt.month, dt.year, plc.county_id, c.county_desc
FROM deposition d
LEFT JOIN
person p
ON p.id =
(
SELECT id
FROM person pi
WHERE pi.deposition_id = d.deposition_id
ORDER BY
pi.deposition_id, pi.id
LIMIT 1
)
LEFT JOIN
place_link AS pl
ON pl.id =
(
SELECT id
FROM place_link AS pli
WHERE pli.deposition_id = d.deposition_id
ORDER BY
pli.deposition_id, pi.id
LIMIT 1
)
LEFT JOIN
date AS dt
ON dt.id =
(
SELECT id
FROM date AS dti
WHERE dti.deposition_id = d.deposition_id
ORDER BY
dti.deposition_id, pi.id
LIMIT 1
)
LEFT JOIN
place AS plc
ON plc.place_id = pl.place_id
LEFT JOIN
county AS c
ON c.county_id = plc.county_id
WHERE d.manuscript_number = '840'
ORDER BY
d.manuscript_number, d.folio_start
LIMIT 20
Create an index on deposition (manuscript_number, folio_start) for this to work fast
Also create a composite index on (deposition_id, id) on person, place_link and date.
The poor performance is almost certainly from lack of indexes. Your deposition table doesn't have any indexes, and that probably means the other tables you're referencing don't have any either. You can start by adding an index to your deposition table. From the MySQL shell, or phpMyAdmin, issue the following query.
ALTER TABLE deposition ADD INDEX(deposition_id, manuscript_number);
You know you're on the right track if the query executes faster after adding the index. From there you might want to put indexes on the other tables on the referenced columns. For instance for this part of your query "LEFT JOIN person AS p ON p.deposition_id = d.deposition_id", you could try adding an index to the person table using.
ALTER TABLE person ADD INDEX(deposition_id);
You only need a LEFT JOIN if the joined table might not have a matching value. Is it possible in your database schema for a person to not have a matching person_type? Or deposition to not have a matching row in date? A place not have a matching county?
For any of those relationships that must exist for the result to make sense you can change the LEFT JOIN to an INNER JOIN.
These columns should have indexes (unique if possible):
person.deposition_id
date.deposition_id
place_link.deposition_id
place_link.place_id
The date table looks like a bad design; I can't think of a reason to have a table of dates instead of just putting a column of type date (or datetime) in the deposition table. And date is a terrible name for a table because it's a SQL reserved word.

Categories