mysql select a different field is a field is null - mysql

I have a staff table
id int(11)
names varchar(120)
family_names varchar(100)
nickname varchar(100)
and a table of who is at work
id int(11)
personid int(11)
titleid int(11)
typeid int(11)
at_work datetime
status int(11)
My problem is that I need to produce a report where I check who is at work and display names, family name and title. As people are called by nickname, I need to check first if nickname exist so I find this example on this site
SELECT IF(LENGTH(nickname)>0, nickname, names) FROM staff_list
I will get either the nickname or the names, but it do not solve my request as I need to search mu who_is_at_work table and then get the staff at work and create that list.
Is it possible to do a select within a select to get the three fields that I need
nickname/names, family name, title
After a while I figured out how to do it.
I found the solution, thanks for posting. Here is what I ended up doing.
SELECT p.id AS ID,
IF(LENGTH(p.nickname)>0, p.nickname, p.names) AS 'Names/Nickname',
p.family_names AS 'Family name',
r.rank AS 'Rank'
FROM atworklist AS who
LEFT JOIN stafflist as p on p.id = who.personid
LEFT JOIN title AS r ON r.id = p.titleid
where who.shipid= 2 and who.status in (2,3)
ORDER BY r.sortorder
Works like a charm for me

If you want to get rid of the null values you can use coalesce.
select coalesce(nickname, names) from staff_list
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_coalesce
If you want to check for empty strings you can do it this way:
select case when nickname = '' then names else nickname end from staff_list

Related

MYSQL: show registers, relate and rename

The instructions are the following:
Generate a report of all the products purchased by the customers where it appears: the id of the customer, the customer's full name, the city, the state, the ID number, the date of sale, the product code, the product name, the quantity sold and finally a message that says "you paid" or "payment Pending" status depending on the payment status where 0 = paid and 1 = pending. This report should appear sorted alphabetically first by state and then by customer name.
what I tried is this:
select cli_nom, cli_city, cli_state, fac_num, fac_saledate, prod_cod, fac_total, fac_status
where fac_status = 0 as paid and fac_status = 1 as pending
from factures, products, clients order by cli_state, cli_nom, asc;
Wich absolutley didnt work, Im not sure about the sintax to rename or mask a column.
The table structures are the following:
table clientes:
1. cli_nom varchar(100)
2. cli_state varchar(100)
3. cli_city varchar(100)
4. cli_id int(11)
5. cli_status int(11)
6. cli_dateofsale date
table products:
1. prod_cod int(11)
2. prod_categ char(1)
3. prod_nom varchar(100)
4. prod_price double
5. prod_descrip varchar(100)
6. prod_discount float
table facturas:
1. fac_num int(11)
2. fac_datesold date
3. fac_cli_id int(11)
4. fac_status int
5. fac_total float
You are having a trouble with the querys.
When you want to query something, the form of the complete statement is something like this
Select [fields]
from [table(s)] --which means there includes inner joins
where [filter rows]
group by [fields to group]
having [filtering groups]
order by [fields]
Of course, is something much more complicated and bigger than this, but it will give you some initial concepts.
You will always have to respect this order, so in your query are putting a where into the select.
If you want to change something to show, dependending on some evaluation, but you will ALWAYS show something (you are not filtering, you are choosing what to show according to the value), you can use CASE clause.
In this example, you could do something like this
select cli_nom, cli_city, cli_state, fac_num, fac_saledate,
prod_cod, fac_total, fac_status
CASE when fac_status = 0 then 'You Paid'
when fac_status = 1 then 'payment Pending'
else 'Not sure about state' END
from factures
inner join products on --put here how do you relate products with factures
inner join clients on -- put here how do you relate clients with products/factures
order by cli_state, cli_nom, asc;
If you don't know how to use INNER JOIN, here you have some info.
Basically, is a clause that is used to relate two tables.
something like
(..)
from Table1 A
INNER JOIN Table2 B on A.id = B.id
(A and B are aliases, and representing the table that have set).
This means that it will compare every row from Table1 to every row from Table2, and when the condition is matched (in this case id from table1 [A.id] equals id from Table2 [= B.id]) then that relation-row is showed (means that it will show you all the row from table1 + all the row from table2)

Performing joins

So this my first run into mysql databases,
I got a lot of help from my first question :
MYSQL - First Database Structure help Please
and built my database as pitchinnate recommended
I have :
Table structure for table club
Column Type Null Default
id int(11) No
clubname varchar(100) No
address longtext No
phone varchar(12) No
website varchar(255) No
email varchar(100) No
Table structure for table club_county
Column Type Null Default
club_id int(11) No
county_id int(11) No
Table structure for table county
Column Type Null Default
id int(11) No
state_id tinyint(4) No
name varchar(50) No
Table structure for table states
Column Type Null Default
id tinyint(4) No
longstate varchar(20) No
shortstate char(2) No
I set up foreign key relationships for everything above that looks that way.... states.id -> county.state_id for example
What I tried to run :
SELECT *
FROM club
JOIN states
JOIN county
ON county.state_id=states.id
JOIN club_county
ON club_county.club_id=club.id
club_county.county_id=county.id
This didn't work... I'm sure the reason is obvious to those of you who know what SHOULD be done.
What I'm trying to do is
get a listing of all clubs, with their associated state and county(ies)
You need to specify a JOIN condition for each of your joins. It should look something like the following:
SELECT *
FROM club
JOIN club_county ON club.id = club_county.club_id
JOIN county ON club_county.county_id = county.id
JOIN states ON county.state_id = state.id
Your version omitted an ON clause on the line that reads JOIN states.
One thing regarding your table names: It's advisable to stick to either singular or plural table names and not to mix them (notice you have club (singular) and states (plural) tables). This makes things easier to remember when you're developing and you're less likely to make mistakes.
EDIT:
If you want to limit which columns appear in your result, you just need to modify the SELECT clause. Instead if "SELECT *", you comma separate just the fields you want.
E.g.
SELECT club.id, club.name, county.name, states.name
FROM club
JOIN club_county ON club.id = club_county.club_id
JOIN county ON club_county.county_id = county.id
JOIN states ON county.state_id = state.id
The query you have written will not even execute as it has syntax error.
Please see this link for more details on JOINS:
13.2.8.2. JOIN Syntax
Also, `
SELECT *
FROM club a, county b, states c, club_county d
WHERE a.id = d.county_id
AND b.id = d.county_id
AND b.state_id = c.id
`
I hope this will help... If you still need help, please let us know...
Thanks...
Mr.777
So after you have modified the question, now the answer would be more like:
SELECT club.id,club.clubname,county.name,states.longstate,states.shortstate
FROM club,club_county,county,states
WHERE club.id=club_county.club_id
AND county.id=club_county.county_id
AND states.id = county.state_id
Please let me know if you need more help...
Thanks...
Mr.777

Multiple MySQL Nested Selects

Hi there I wonder if someone could help, I am having a complete mental block.
I have this select statment that works perfectly, however I now want to check the results (FirstName and Surname) against another table to find out more details about them. I can't for the life of me figure how to do that.
SELECT `FirstName` , `Surname` , MobilePhone
FROM users
WHERE NOT
EXISTS (
SELECT PhoneNumber
FROM Orangedata
WHERE users.MobilePhone = orangedata.`PhoneNumber`
)
This select returns me a list of records (Name and Mobiles) that I want to run another select with.
The Result looks like
FirstName Surname Mobile
I want to run my next Select that says
Select *
from Table 3
WHERE FirstName = FirstName from previous select AND WHERE Surname = Surname from previous select.
The overall result I am looking for is
Give me all the details (from Table 3) for a user that does not have any record in table Orangedata
Its effectively three nested selects (I think) and I just can't figure it out
SELECT table3.* FROM
table3
JOIN
(SELECT `FirstName` , `Surname` , MobilePhone
FROM users
WHERE NOT
EXISTS (
SELECT PhoneNumber
FROM Orangedata
WHERE users.MobilePhone = orangedata.`PhoneNumber`
)) as b
ON (table3.FirstName = b.FirstName AND table3.Surname = b.Surname))
Or something like this

Database Query by Keywords

I have following tables:
products_match:
atcode varchar(6)
valcode varchar(100)
id_prod varchar(15)
products:
asin varchar(15)
title varchar(155)
Example content of products_match table:
atcode='type'
valcode='wifi'
id_prod='1SC52DD'
atcode='type'
valcode='ram'
id_prod='11DD5ER'
There are multiple kwywords in this table.
I'm budilding a simple search engine - I need to display products matching multiple criteria, example:
select products where atcode='type' AND valcode='wifi' AND valcode='brand' AND 'valcode' = 'Sony'
Do I need to apply self joins for every group of arguments here?
Right now I have following query:
SELECT * FROM products_match a
JOIN products b ON a.id_prod=b.asin
JOIN assortment_match c ON a.id_prod=c.id_prod
WHERE c.atcode='brand' AND c.valcode='sony'
ORDER BY sales_rank ASC LIMIT 0,60
however it returns no products.
Can anybody help me solve this issue?
Edit
I've been told that I should use one self join for every group of keywords. What do you think?
One method is that for each match, you could use an EXISTS subquery.
AND EXISTS (select 1 from products_match
where id_prod = a.id_prod
and atcode = 'type' and valcode = 'wifi')

optimising and scaling mysql structure + queries for large mailing groups

So I have a system that stores contacts and allows them to be put into groups. These groups can be defined by criteria (everyone with surname 'smith'), or by explicitly adding / excluding people.
The problem I am having is that when I list the mailing groups, I need to count how many contacts are in each one. This number can change as contacts are added / removed from the contacts table. On small groups / amounts of contacts it is fine, however using 50k ish contacts runs into problems
An example query I use for this is as follows:
SELECT COUNT(c_id) FROM contacts, mgroups
LEFT JOIN mgroups_explicit ON mg_id = me_mg_id
WHERE mgroups.site_id = '10'
AND mg_id = '20'
AND me_c_id = c_id
AND contacts.site_id = '10'
OR (contacts.site_id = '10' AND ( c_tags LIKE '%tag1%')) AND c_id NOT IN
( SELECT mex_c_id FROM mgroups_exclude WHERE c_id = mex_c_id ) GROUP BY c_id
The criteria table does not feature in this query, as the problem presents itself when large groups are created explicitly, rather than with a criteria. This is required as criteria based groups grow or shrink on the fly as you modify your contacts, where as explicit is generally set in stone. So in this case, if you explicitly add 20k contacts to a group, it adds 20k rows to the table marked with that mg_id as a foreign key.
This basically takes ages / times out / gets the wrong number / generally doesn't work very well. I either need to figure out a more efficient query, or figure out a better way to store everything.
Any ideas?
The 5 main tables that make up the database
contacts - where the actual contacts reside
Field Type Null Default Comments
c_id int(8) No
site_id int(6) No
c_email varchar(500) No
c_source varchar(255) No
c_subscribed tinyint(1) No 0
c_special tinyint(1) No 0
c_domain text No
c_title varchar(12) No
c_name varchar(128) No
c_surname varchar(128) No
c_company varchar(128) No
c_jtitle text No
c_ad1 text No
c_ad2 text No
c_ad3 text No
c_county varchar(64) No
c_city varchar(128) No
c_postcode varchar(32) No
c_lat varchar(100) No
c_lng varchar(100) No
c_country varchar(64) No
c_tel varchar(20) No
c_mob varchar(20) No
c_dob date No
c_registered datetime No
c_updated datetime No
c_twitter varchar(255) No
c_facebook varchar(255) No
c_tags text No
c_special_1 text No
c_special_2 text No
c_special_3 text No
c_special_4 text No
c_special_5 text No
c_special_6 text No
c_special_7 text No
c_special_8 text No
mgroups - basic mailing group info
Field Type Null Default Comments
mg_id int(8) No
site_id int(6) No
mg_name varchar(255) No
mg_created datetime No
mgroups_criteria - criteria for said mailing groups
Field Type Null Default Comments
mc_id int(8) No
site_id int(6) No
mc_mg_id int(8) No
mc_criteria text No
mgroups_exclude - anyone to exclude from criteria
Field Type Null Default Comments
mex_id int(8) No
site_id int(6) No
mex_c_id int(8) No
mex_mg_id int(8) No
mgroups_explicit - anyone to explicitly add without the use of criteria
Field Type Null Default Comments
me_id int(8) No
site_id int(6) No
me_c_id int(8) No
me_mg_id int(8) No
And the indexs / explain of query. Must admit, indexes are not my strong point, any improvements?
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY mgroups ALL PRIMARY,mg_id NULL NULL NULL 9 Using temporary; Using filesort
1 PRIMARY mgroups_explicit ref me_mg_id me_mg_id 4 engine_4.mgroups.mg_id 8750
1 PRIMARY contacts ALL PRIMARY,c_id NULL NULL NULL 86012 Using where; Using join buffer
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const table...
I don't see any indexes in the schema above, you do have indexes don't you?
run an explain on the query
EXPLAIN
SELECT COUNT(c_id) FROM
contacts, mgroups LEFT JOIN mgroups_explicit ON mg_id = me_mg_id
WHERE
mgroups.site_id = '10'
AND mg_id = '20'
AND me_c_id = c_id
AND contacts.site_id = '10'
OR (contacts.site_id = '10'
AND ( c_tags LIKE '%tag1%'))
AND c_id NOT IN (SELECT mex_c_id FROM mgroups_exclude WHERE c_id = mex_c_id ) GROUP BY c_id
That will tell you about what indexes are being used how many records it has to sort through etc..
DC
Right so I got this answered elsewhere (Huge thanks to Hambut_Bulge), so for the sake of it being useful to anyone else heres the solution:
First things off you're mixing old and new (ANSI) style joins in the same query. This is considered a bad idea in SQL circles. By old style I mean we write a query with a join along these lines
SELECT a.column_name, b.column2
FROM table1 a, second_table b
WHERE a.id_key = b.fid_key
AND b.some_other_criteria = 'Y';
In the newer ANSI style we'd rewrite the above to this:
SELECT a.column_name, b.column2
FROM table1 a INNER JOIN second_table b ON a.id_key = b.fid_key
WHERE b.some_other_criteria = 'Y';
Its neater and easier to read which bits are join conditions and which are where clauses. Its also best to get into the habit of using ANSI style as old style support may (at some point) be discontinued.
Also try and be consistent in your use of dot notation and/or aliases. Again it makes big queries easier to read.
Back to your problem query, I began by starting to convert it into ANSI style and straight-away noticed that you don't have a join condition between contacts and mgroups. This means that optimizer will create a cross join (also called a cartesian product), which was probably something you don't want to do. The cross join (in case you didn't know) joins every row in the contacts table with every row in the mgroups table. So if you have 50,000 rows in contacts and 20,000 rows in mgroup you're going to get a joined result set containing 1,000,000,000 rows!
The other thing that is going to slow this query drastically is the subquery on mgroups_exclude. A subquery is executed once for each row in the outer query eg:
SELECT a.column1
FROM table1 a
WHERE a.id_key NOT IN ( SELECT * FROM table2 b WHERE a.id_key = b.fid_key);
Assume that table1 has 2,000,000 rows and table2 has 500,000. For each and every row in the outer query (table1) the database is going to have to do a full scan on the inner query. So to get a result the database will have read 1,000,000,000,000 rows and we may only be interested in 1,000! It will not touch any indexes no matter what.
To get around this we can use a left join (also called a left outer join) on the two tables.
SELECT a.column1
FROM table1 a LEFT JOIN table2 b ON a.id_key = b.fid_key
WHERE b.fid_key IS NULL;
An outer join does not require each record in the joined tables to have a matching record. So the example above we'd get all the records from table1 even if there is no match on table2. For non-matched records the database returns a NULL and we can test for that in the where clause. Now the optimizer can scan the indexes on the two tables id_key fields (assuming there are any), resulting in a much faster query.
So, to wrap up. I'd rewrite your orginal query thus:
SELECT COUNT( a.c_id )
FROM contacts a
INNER JOIN mgroups b ON a.c_id = b.mg_id
LEFT JOIN mgroups_explicit c ON b.mg_id = c.me_mg_id
LEFT JOIN mgroups_exclude d ON a.c_id = d.mex_c_id
WHERE b.mg_id = '20'
AND a.site_id = '10'
AND a.c_tags LIKE '%tag1%'
AND d.mex_c_id IS NULL
GROUP BY c_id;