How to change the order of rows in order to have the rows ordered by custom_field?
For example I have got table with id asc and would like to have the rows in the desc order. I need it in order not to use the 'order by id desc' in the mysql query to optimize the query speed.
I tried:
insert table_with_ordered_rows()
select * from table_with_not_ordered_rows order by id desc;
but it just copy the table with standard asc order.
The only way to get the sorted result set in mysql - is to add ORDER BY in query.
in the mysql query to optimize the query speed.
It is incorrect. If you have issues with query performance - then ask about query performance. Give us complete table structure, the query, the explain and statistics about data.
The only way to guarantee order in a result set is to use an ORDER BY clause. An ORDER BY can make use of an index, if one exists...
That said, the following worked for me on MySQL 5.1.49:
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`col` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1$$
INSERT INTO t1 (col) VALUES ('a'),('b'),('c');
Result set:
id col
--------
1 a
2 b
3 c
Intermediate table:
CREATE TABLE `t2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`col` varchar(45) DEFAULT NULL,
`old_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1$$
INSERT INTO t2 (col, old_id)
SELECT t.col, t.id
FROM t1 t
ORDER BY t.id DESC
Result set:
id col old_id
----------------
1 c 3
2 b 2
3 a 1
ALTER `tablename` ORDER BY `orderField` DESC;
But you must execute this query after each insert. See 13.1.7. ALTER TABLE Syntax.
Related
I have 2 separate tables, both of which I need to query simultaneously to get the correct information to display. The tables are members and posts. Through an html form, a user enters criteria for the members table, and then I need to use the primary index of all those specific members to find all the posts submitted by those members and then do a sort on the posts table results. The results will be a mixture of rows from the two tables. Both tables have a primary index of the name 'id'. So far what I've come up with is:
$sql_get_posts = mysqli_query($link, "(SELECT id, username FROM members WHERE active='y' AND gender='M' AND city='Yuma' AND state='Arizona') UNION (SELECT * FROM posts WHERE member_id='id' AND active='y' ORDER BY list_weight DESC)") or die(mysqli_error($link));
The error I'm getting is "The used SELECT statements have a different number of columns".
I need to then cycle through the returned results from both tables to populate the content seen by the user:
<?php
while ($row = mysqli_fetch_array($sql_get_posts)) {
$post_id = $row['id']; //This should be the post primary index named 'id', not the member primary index also name 'id'
$member_id = $row['member_id']; //This is the member_id row in the post table referencing this particular member who wrote this post
$member_username = $row['username']; //This is a row stored in the member table
$title = $row['title']; //This is a row stored in post table
******//and on and on getting rows from only the post table
}
Edit My SQL tables:
CREATE TABLE IF NOT EXISTS `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`age` varchar(3) NOT NULL,
`gender` varchar(1) NOT NULL,
`city` varchar(20) NOT NULL,
`state` varchar(50) NOT NULL,
`active` enum('y','n') NOT NULL DEFAULT 'y',
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`member_id` int(11) NOT NULL,
`title` text NOT NULL,
`comments` enum('y','n') NOT NULL DEFAULT 'y',
`post_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`list_weight` double NOT NULL,
`active` enum('y','n') NOT NULL DEFAULT 'y',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=47 ;
Use join instead of union, union assumes the tables you're combining are similar, whereas join merges the columns of two tables.
Something like:
SELECT members.id, members.username, posts.*
FROM members
INNER JOIN posts
ON members.id = posts.member_id
WHERE members.active='y' AND members.gender='M' AND members.city='Yuma' AND members.state='Arizona'
ORDER BY posts.list_weight DESC
SELECT statement within the UNION must have the same number of columns. The columns must also have similar data types. Also, the columns in each SELECT statement must be in the same order.
But you have selected only 2 columns in first query and "*" for second select query
use joins
SELECT m.id, m.username,p.* FROM members m JOIN posts p on m.active='y' AND m.gender='M' AND m.city='Yuma' AND m.state='Arizona' and p.member_id='id' AND p.active='y' ORDER BY p.list_weight DESC
or you can use
SELECT m.id, m.username,p.* FROM members m,posts p where m.active='y' AND m.gender='M' AND m.city='Yuma' AND m.state='Arizona' and p.member_id='id' AND p.active='y' ORDER BY p.list_weight DESC
I am wanting to select the 3 biggest selling records with this is my table
CREATE TABLE IF NOT EXISTS `contas` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_service` int(11) DEFAULT NULL,
`data` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=28 ;
the 'id_service' is the main column, the more sales, more records are added with the same 'id_service'.
so how do I do this without using PHP and select in descending order?
I tried this
select id_service, count(*) as id_service
from vendas WHERE id_service is not null
group by id_service order by id_service desc LIMIT 3
You have aliased both columns to the same name. No wonder the query is confused. Try this:
select id_service, count(*) as cnt
from vendas WHERE id_service is not null
group by id_service
order by cnt desc
LIMIT 3;
I can use LIMIT to surrender myself to one row but I would like to learn how I can provide one of each rows rather than the several of the same rows / data
Goto : http://inks-etc.com/Script/SQL/loginform.php
Username: jason
Password: 123
Sounds like you have this setup:
create table `some_table` (
`uniq_id` bigint(20) unsigned not null auto_incremenet,
`some_data` varchar(200) not null,
`some_other_data` varchar(100) not null,
PRIMARY KEY (`uniq_id`)
) ENGINE=MyISAM AUTO_INCREMENT=260285 DEFAULT CHARSET=utf8;
create table `some_other_table` (
`other_id` bigint(20) unsigned not null auto_incremenet,
`link_to_uniq_id` bigint(20) unsigned not null,
`multiple_entry_data` varchar(100) not null,
PRIMARY KEY (`uniq_id`)
) ENGINE=MyISAM AUTO_INCREMENT=260285 DEFAULT CHARSET=utf8;
Where some_other_table can match some_table multiple times. It also sounds like you are trying something like this, and wanting one row per uniq_id:
select * from some_table
inner join some_other_table
on some_table.uniq_id = some_other_table.link_to_uniq_id
The way that you can modify this type of query to get only one entry per uniq_id is something like this, using the group by directive:
select * from some_table
inner join some_other_table
on some_table.uniq_id = some_other_table.link_to_uniq_id
group by some_table.uniq_id
Hopefully I understood your question, and hopefully this answers it.
I am trying to make a printable page, where there is all the sales of a specified manufacturer, listing all the products, between specified dates. If there has not been any sales, it should display 0.
The tables
// Manufacturer table
// mid, manufacturer
// Products table
// pid, product, ref_manufacturer_id
// Orders table
// oid, orderPrice, orderDateTime, ref_product_id
And the query that works (without date limitation)
SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord ON pord.ref_product_id = prod.pid
WHERE manu.mid = :manu_id
GROUP BY prod.product;
But as soon as I add into the WHERE-syntax this
WHERE manu.mid = :manu_id AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end
I am using PHP PDO on connecting and verifying that the manu_id is int and the orders_start/end is converted to MySQL date format.
But the question I am trying to fidn out is, what is causing the problem, that when I add the date restriction, every product that was not ordered, is not displayed on the output?
SQL on creating the tables
CREATE TABLE product_list (
pid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
product varchar(255) NOT NULL,
ref_manufacturer_id bigint(20) unsigned NOT NULL,
PRIMARY KEY (pid),
KEY ref_manufacturer_id (ref_manufacturer_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE product_manufacturer (
mid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
manufacturer varchar(255) NOT NULL,
PRIMARY KEY (mid),
UNIQUE KEY manufacturer (manufacturer)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE product_orders (
oid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
orderPrice float(10,2) NOT NULL,
orderDatetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
red_product_id bigint(20) unsigned NOT NULL,
PRIMARY KEY (oid),
KEY red_product_id (red_product_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
What you need is to move the orderDateTime criteria to the join clause instead of where clause like:
SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord
ON pord.ref_product_id = prod.pid
AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end
WHERE manu.mid = :manu_id
GROUP BY prod.product;
The reason it does not work within the WHERE clause is because of the NULL values returned from the outer join. When you do not have a row in product_orders fot a product, the outer join returns a NULL for the date field orderDateTime and that row will be filtered out because a NULL is not equal to anything.
Try:
SELECT p.product,
COALESCE(o.orderCount, 0) as orderCount,
COALESCE(o.orderSum,0) AS orderSum
FROM product_manufacturer AS m
JOIN product_list AS p ON p.ref_manufacturer_id = m.mid
LEFT JOIN (
SELECT ref_product_id as pid, COUNT(oid) AS orderCount, SUM(orderPrice) AS orderSum
FROM product_orders
WHERE DATE(orderDateTime) BETWEEN :orders_start AND :orders_end
GROUP BY ref_product_id
) AS o ON p.pid = o.pid
WHERE m.mid = :manu_id
Edit: Corrected after ypercube comment.
try this on the where clause.
WHERE manu.mid = :manu_id AND (DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end)
It might be reading the second AND function as another where clause that the statement should return true. Just a hunch on that. Let me know if this does the trick.
I don't know how your specific system works, but it may be orderDateTime is not set (ie, NULL or something else) until that product gets ordered. You may want to try:
WHERE manu.mid = :manu_id AND ((DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end) OR pord.orderDateTime=NULL)
If this is not the case, could you give an example of the orderDateTime value for something that is not showing up when you want it to?
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.