AS or not to AS, queries - php

I'm fairly new to PHP/MySql and using queries in general. I was just wondering if there's any benefit to using "AS" in a query other than trying to make it look cleaner? Does it speed up the query at all? I probably could have figured this out by a google search but I wanted to ask my first question and see how this works. I WILL select an answer (unlike some people...)
with:
SELECT
news.id as id
news.name as name
FROM news
without:
SELECT
news.id
news.name
FROM news
A more complex example from a many-to-many relationship tutorial I found:
SELECT
c.name,
cf.title
FROM celebrities AS c
JOIN (
SELECT
icf.c_id,
icf.f_id,
f.title
FROM int_cf AS icf
JOIN films AS f
ON icf.f_id = f.f_id
) AS cf
ON c.c_id = cf.c_id
ORDER BY c.c_id ASC

There's no reason to use it if you know there will be no conflicts with other columns. There are no differences in performance, but it does change the name of the output column. It's really useful for when you construct dynamic selects. So for instance, if you had a first_name and a last_name field, you could use the CONCAT function to do:
SELECT CONCAT(first_name, ' ', last_name) AS name FROM whatever
Furthermore, the AS operator works when defining sub-query tables, as you showed in that JOIN. Without that AS, you wouldn't be able to reference that table in the ON clause or the SELECT fields.

1) No, it doesn't affect performance one way or the other
2) As far as readability of the SQL Query, it's just a matter of preference
3) It does affect the readability of the output (it changes the column headers)

Only with regards to ambiguity & reserved words. It does not affect performance. Ambiguity refers to two columns with the same names coming from separate tables. Other RDBMS's will slap you for this. Reserved words refers to system words that must either be escaped using backticks or aliased.
With subqueries, aliased columns may not be derived past the child level.

Speed of query execution when you use aliases ("AS" keyword) and when you dont use them is the same. Reasons for using them:
it makes it easier to work with databases if you have very long or complex table names or column names:
SELECT c.Construction_ID, c.Name, c.Price
FROM Constructions c LEFT JOIN
Constructions_Orders co ON c.Construction_ID = co.Construction_ID
WHERE c.OrderID='1' AND c.Name='ConstructionName'
However, it changes output columns names, take it into account too (makes it harder for person who is reading code).
Aliases are useful for creating dynamic selects:
mysql> SELECT CONCAT(id, name, work_date) as profile
-> FROM employee_tbl;
+-----------------------------+
| profile |
+-----------------------------+
| 1John2007-01-24 |
| 2Ram2007-05-27 |
| 3Jack2007-05-06 |
| 3Jack2007-04-06 |
| 4Jill2007-04-06 |
| 5Zara2007-06-06 |
| 5Zara2007-02-06 |
+-----------------------------+
7 rows in set (0.00 sec)

Related

mysql like query exclude numbers

I have a small problem with a php mysql query, I am looking for help.
I have a family tree table, where I am storing for each person his/her ancestors id separated by a comma. like so
id ancestors
10 1,3,4,5
So the person of id 10 is fathered by id 5 who is fathered by id 4 who is fathered by 3 etc...
Now I wish to select all the people who have id x in their ancestors, so the query will be something like:
select * from people where ancestors like '%x%'
Now this would work fine except, if id x is lets say 2, and a record has an ancestor id 32, this like query will retrieve 32 because 32 contains 2. And if I use '%,x,%' (include commas) the query will ignore the records whose ancestor x is on either edge(left or right) of the column. It will also ignore the records whose x is the only ancestor since no commas are present.
So in short, I need a like query that looks up an expression that either is surrounded by commas or not surrounded by anything. Or a query that gets the regular expression provided that no numbers are around. And I need it as efficient as possible (I suck at writing regular expressions)
Thank you.
Edit: Okay guys, help me come up with a better schema.
You are not storing your data in a proper way. Anyway, if you still want to use this schema you should use FIND_IN_SET instead of LIKE to avoid undesired results.
SELECT *
FROM mytable
WHERE FIND_IN_SET(2, ancestors) <> 0
You should consider redesigning your database structure. Add new table "ancestors" to database with columns:
id id_person ancestor
1 10 1
2 10 3
3 10 4
After -- use JOIN query with "WHERE IN" to choose right rows.
You're having this issue because of wrong design of database.First DBMS based db's aren't meant for this kind of data,graph based db's are more likely to fit for this kind of solution.
if it contain small amount of data you could use mysql but still the design is still wrong,if you only care about their 'father' then just add a column to person (or what ever you call it) table. if its null - has no father/unknown otherwise - contains (int) of his parent.
In case you need more then just 'father' relationship you could use a pivot table to contain two persons relationship but thats not a simple task to do.
There are a few established ways of storing hierarchical data in RDBMS. I've found this slideshow to be very helpful in the past:
Models for Hierarchical Design
Since the data deals with ancestry - and therefore you wouldn't expect it to change that often - a closure table could fit the bill.
Whatever model you choose, be sure to look around and see if someone else has already implemented it.
You could store your values as a JSON Array
id | ancestors
10 | {"1","3","4","5"}
and then query as follows:
$query = 'select * from people where ancestors like \'%"x"%\'';
Better is of course using a mapping table for your many-to-many relation
You can do this with regexp:
SELECT * FROM mytable WHERE name REGEXP ',?(x),?'
where x is your searched value
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,ancestors VARCHAR(250) NOT NULL
);
INSERT INTO my_table VALUES(10,',1,3,4,5');
SELECT *
FROM my_table
WHERE CONCAT(ancestors,',') LIKE '%,5,%';
+----+-----------+
| id | ancestors |
+----+-----------+
| 10 | ,1,3,4,5 |
+----+-----------+
SELECT *
FROM my_table
WHERE CONCAT(ancestors,',') LIKE '%,4,%';
+----+-----------+
| id | ancestors |
+----+-----------+
| 10 | ,1,3,4,5 |
+----+-----------+

MySQL SELECT SUM(Column) and SELECT * Cardinality violation: 1241 Operand should contain 1 column(s)

Trying to write statement where in single statement select all (*) and sum one column from the same database and the same table, depending on conditions.
Wrote such statement (based on this Multiple select statements in Single query)
SELECT ( SELECT SUM(Amount) FROM 2_1_journal), ( SELECT * FROM 2_1_journal WHERE TransactionPartnerName = ? )
I understand that SELECT SUM(Amount) FROM 2_1_journal will sum all values in column Amount (not based on codition).
But at first want to understand what is correct statement
With above statement get error SQLSTATE[21000]: Cardinality violation: 1241 Operand should contain 1 column(s)
Can not understand error message. From advice here MySQL - Operand should contain 1 column(s) understand that subquery SELECT * FROM 2_1_journal WHERE TransactionPartnerName = ? must select only one column?
Tried to change statement to this SELECT ( SELECT * FROM 2_1_journal WHERE TransactionPartnerName = ? ), ( SELECT SUM(Amount) FROM 2_1_journal), but get the same error...
What would be correct statement?
SELECT *, (SELECT SUM(Amount) FROM 2_1_journal)
FROM 2_1_journal
WHERE TransactionPartnerName = ?
This selects sums up Amount from the entire table and "appends" all rows where TransactionPartnerName is the parameter you bind in the client code.
If you want to limit the sum to the same criteria as the rows you select, just include it:
SELECT *, (SELECT SUM(Amount) FROM 2_1_journal WHERE TransactionPartnerName = ?)
FROM 2_1_journal
WHERE TransactionPartnerName = ?
A whole different thing: table names like 2_1_journal are strong indicators of a broken database design. If you can redo it, you should look into how to normalize the database properly. It is most likely pay back many times over.
With regard to normalization (added later):
Since the current design uses keys in table names (such as the 2 and 1 in 2_1_journal), I'll quickly illustrate how I think you can vastly improve that design. Lets say that the table 2_1_journal has the following data (I'm just guessing here because the tables haven't been described anywhere yet):
title | posted | content
------+------------+-----------------
Yes! | 2013-01-01 | This is just a test
2nd | 2013-01-02 | Another test
This stuff belongs to user 2 in company 1. But hey! If you look at the rows, the fact that this data belongs to user 2 in company 1 is nowhere to be found.
The problem is that this design violates one of the most basic principles of database design: don't use keys in object (here: table) names. A clear indication that something is very wrong is if you have to create new tables if something new is added. In this case, adding a new user or a new company requires adding new tables.
This issue is easilly fixed. Create one table named journal. Next, use the same columns, but add another two:
company | user | title | posted | content
--------+------+-------+------------+-----------------
1 | 2 | Yes! | 2013-01-01 | This is just a test
1 | 2 | 2nd | 2013-01-02 | Another test
Doing it like this means:
You never add or modify tables unless the application changes.
Doing joins across companies or users (and anything else that used to be part of the table naming scheme is now possible with a single, fairly simple select statement).
Enforcing integrity is easy - if you upgrade the application and want to change the tables, the changes doesn't have to be repeated for each company and user. More importantly, this lowers the risk of having the application get out of sync with the tables in the database (such as adding the field comments to all x_y_journal tables, but forgetting 5313_4324_journal causing the application to break only when user 5313 logs in. This is the kind of problem you don't want to deal with.
I am not writing this because it is a matter of personal taste. Databases are just designed to handle tables that are laid out as I describe above. The design where you use object keys as part of table names has a host of other problems associated with it that are very hard to deal with.

MYSQL UNION across 3 tables with ORDER BY

We have this statement:
(SELECT res_bev.bev_id, property_value.name AS priority
FROM res_bev, bev_property, property_value
WHERE res_bev.res_id='$resIn'
AND bev_property.bev_id=res_bev.bev_id
AND bev_property.type_id='23'
AND property_value.id=bev_property.val_id)
UNION
(SELECT res_bev.bev_id, property_value.name as priority
FROM res_bev, bev_property, property_value
WHERE res_bev.res_id='$resIn'
AND bev_property.bev_id=res_bev.bev_id
AND bev_property.type_id='22'
AND property_value.id=bev_property.val_id)
We have Three Tables:
Res_bev
res_id | bev_id | id
Bev_property
type_id | val_id | bev_id | id
Property_value
name | id
What I am looking for is the results to be ordered by glass price(type_id='23') then bottle price(type_id='22') however it seems the union includes duplicates due to fact the first select returns say 3456 | 7.5 and the second returns 3456 | 55 since the price/Glass is 7.5 and the price/Bottle is 55; how can I eliminate these duplicates form the second SQL statement to return and ordered table?
Also, fooled with creating a pseudo-table via left joins to create a table of bev_id | price/Glass | price/Bottle, however since this should be able to expand to multiple price types I figured a UNION would be more efficient. Just a push in the right direction would be helpful.
You can do it in 1 query by specifying bev_property.type_id to match against an IN() clause with the values inside.
To return only the first one found you should require a DISTINCT SELECT of the accompagnying field bev_id.
To ORDER them just add an appropriate descending ORDER BY clause. This should order first and the filter out the second bev_property.type_id value. (Databases never return anything in a specific order unless you tell them to, some might have an internal convention or it might appear they do but this is never guaranteed to be repeatable unless you specify an ORDER BY clause in your SELECT statement. )
SELECT DISTINCT res_bev.bev_id, property_value.name AS priority
FROM res_bev, bev_property, property_value
WHERE res_bev.res_id='$resIn'
AND bev_property.bev_id=res_bev.bev_id
AND bev_property.type_id IN ('23','22')
AND property_value.id=bev_property.val_id
ORDER BY bev_property.type_id DESC;
A UNION won't really be faster since you'd have to do the whole lookup twice and if you don't have this field indexed then you'll do a whole table traversal with match against 1 element twice as opposed do 1 table traversal that matches against 2 elements. (walking over a whole table is what's generally slow, not matching simple elements against each other)
When properly indexed I think you might have a tiny overhead of executing a new select query and the query analyzer running again but I don't know for sure. It'll probably be smart enough to recognise the similarities between the queries so it won't matter.
It doesn't always hurt to try on specific databases though. Whenever you try query optimisation with different statements use them with EXPLAIN, this will show you what the query will be doing and wether it'll go over whole tables, sort data on file, etc...
Unless I'm missing something, or you have from your question
SELECT res_bev.bev_id,
property_value.name AS priority
FROM res_bev, bev_property, property_value
WHERE res_bev.res_id='$resIn'
AND bev_property.bev_id=res_bev.bev_id
AND (bev_property.type_id='23' OR bev_property.type_id='22')
AND property_value.id=bev_property.val_id)
order by bev_property.type_id desc
PS if you want to order a union
try something along the lines of
Select * from
(
select ...
Union
select ...
) somenameforqryinparentheses
Order by Somecolumn1, somecolumn2

Select random row per distinct field value?

I have a MySQL query that results in something like this:
person | some_info
==================
bob | pphsmbf24
bob | rz72nixdy
bob | rbqqarywk
john | kif9adxxn
john | 77tp431p4
john | hx4t0e76j
john | 4yiomqv4i
alex | n25pz8z83
alex | orq9w7c24
alex | beuz1p133
etc...
(This is just a simplified example. In reality there are about 5000 rows in my results).
What I need to do is go through each person in the list (bob, john, alex, etc...) and pull out a row from their set of results. The row I pull out is sort of random but sort of also based on a loose set of conditions. It's not really important to specify the conditions here so I'll just say it's a random row for the example.
Anyways, using PHP, this solution is pretty simple. I make my query and get 5000 rows back and iterate through them pulling out my random row for each person. Easy.
However, I'm wondering if it's possible to get what I would from only a MySQL query so that I don't have to use PHP to iterate through the results and pull out my random rows.
I have a feeling it might involve a BUNCH of subselects, like one for each person, in which case that solution would be more time, resource and bandwidth intensive than my current solution.
Is there a clever query that can accomplish this all in one command?
Here is an SQLFiddle that you can play with.
To get a random value for a distinct name use
SELECT r.name,
(SELECT r1.some_info FROM test AS r1 WHERE r.name=r1.name ORDER BY rand() LIMIT 1) AS 'some_info'
FROM test AS r
GROUP BY r.name ;
Put this query as it stands in your sqlfiddle and it will work
Im using r and r1 as table alias names. This will also use a subquery to select a random some_info for the name
SQL Fiddle is here
My first response would be to use php to generate a random number:
$randId = rand($min, $max);
Then run a SQL query that only gets the record where your index equals $randID.
Here is the solution:
select person, acting from personel where id in (
select lim from
(select count(person) c, min(id) i, cast(rand()*(count(person)-1) +min(id)
as unsigned) lim from personel group by person order by i) t1
)
The table used in the example is below:
create table personel (
id int(11) not null auto_increment,
person char(16),
acting char(19),
primary key(id)
);
insert into personel (person,acting) values
('john','abd'),('john','aabd'),('john','adbd'),('john','abfd'),
('alex','ab2d'),('alex','abd3'),('alex','ab4d'),('alex','a6bd'),
('max','ab2d'),('max','abd3'),('max','ab4d'),('max','a6bd'),
('jimmy','ab2d'),('jimmy','abd3'),('jimmy','ab4d'),('jimmy','a6bd');
You can limit the number of queries, and order by "rand()" to get your desired result.
Perhaps if you tried something like this:
SELECT name, some_info
FROM test
WHERE name = 'tara'
ORDER BY rand()
LIMIT 1

PHP MySQL Getting Data Out of Multiple Tables

I asked a similar question yesterday which may have been poorly worded, either way I didn't understand and this is something I really need to crack :) I've never done it before and would be very useful for so many of my projects.
This is for a directory website. I have three tables: entry, location, and entry-locations. entry contains information about a building such as name, address, image, etc. location is simply a list of possible locations each building could be. The location table is pretty much irrelevant for this example, it just contains information about the location which I could display on other areas of the site.
entry-locations is a table which links the entries to the locations. It only has two fields, entry-id and location... If you're wondering why I need a seperate table for this is because the same building could have multiple locations (don't ask).
Basically, what I need to do is display listings from each location it's own page. For example, I need to list every building in France, so the query needs to go through the entry-locations table returning every record with the location 'France', then it needs to pull all the data from the entry table corresponding to the entry-id's returned.
I'm sure there is a way to do this with one query and would be extremely greatful if I could be shown how, I could replicate this in so many projects.
How about this one?
-- // Selects all the columns from both entry-locations and entry
SELECT *
FROM entry-locations
JOIN entry e ON e.id = el.entry-id
WHERE el.location = 'France';
-- // To just get the entry data for matching records: (Remove DISTINCT if you
-- // don't mind, or want, each entry with multiple locations in the result set
-- // multiple times)
SELECT DISTINCT e.*
FROM entry-locations el
JOIN entry e ON e.id = el.entry-id
WHERE el.location = 'France'
Edit: Ok, so I tried removing the location table, and instead used entry-location.location as the name of the location.. Is this correct?
Imagine you have this data:
Entry:
|id|name|
| 1|Foo |
| 2|Bar |
Entry-Location:
|entry-id|location|
|1 |France |
|2 |Greece |
|2 |France |
This is how I understand the tables from your description. A more common approach is to have
Entry(id,name)
Location(id,name)
Entry_Location(entry_id, location_id)
This is also the source of some of the confusions in the other posts, I think.
Now, ask MySql to fetch data from both tables, where the id's match up.
SELECT entry.*
FROM `entry`, `entry-location` as el
WHERE entry.id = el.`entry-id`
AND el.location = 'France';
MySql now treats your data like one table, looking like this:
|entry.id|entry.name|el.location|
| 1| Foo| France|
| 1| Foo| Greece|
| 2| Bar| France|
And from that table it selects the entries where el.location = 'France', and returns the specified fields.
This query fetches all the fields from the entry table that matches the requirements you set.
First it makes MySql think of the two tables as one table, by SELECT-ing from both of them.
Have a look at MySql's SELECT reference.
select * from entry where id in (select entry-id from entry-locations)
If I got your idea, you need:
select e.* frmm entry join entry-locations as as l ON l.entry-id=e.id WHERE l.location='France'

Categories