getting non-distinct results from a distinct mysql query - php

Right, another question on queries (there must be a syntax guide more helpful than mySQL's manual, surely?)
I have this query (from another helpful answer on SO)...
SELECT DATE_FORMAT(`when`, '%e_%c_%Y')date, COUNT(`ip`) AddressCount FROM `Metrics` WHERE `ID` = '1' GROUP BY DATE(`when`)
I now want to do a similar query to get unique/distinct results for the IPs... i.e. unique visitors per date. My query was this...
SELECT DATE_FORMAT(`when`, '%e_%c_%Y')date, COUNT(distinct `ip`) AddressCount FROM `Metrics` WHERE `ID` = '1' GROUP BY DATE(`when`)
However, that returns a repetition of dates, though different quantities of Addresscount...
date AddressCount
29_6_2009 1
30_6_2009 1
29_6_2009 1
30_6_2009 1
29_6_2009 1
NULL 1
15_5_2009 1
14_5_2009 2
NULL 3
14_5_2009 4
15_5_2009 1
26_6_2009 1
29_6_2009 1
26_6_2009 1
15_5_2009 1
26_6_2009 1
29_6_2009 1
Any ideas on where I'm going wrong?

Your group by will need to match the data you're selecting, so this should work:
SELECT DATE_FORMAT(`when`, '%e_%c_%Y')date, COUNT(distinct `ip`) AddressCount FROM `Metrics` WHERE `ID` = '1' GROUP BY date

Try
SELECT DATE_FORMAT(when, '%e_%c_%Y')date, COUNT(distinct ip) AddressCount FROM Metrics WHERE ID = '1' GROUP BY date(when)
You might have run into some bugs when using reserved words in MySQL

Related

Creating an INDEX to improve slow execution time caused by ORDER BY

I have the following query in a PHP function. This gets called a number of times depending on a number of factors, but even if it is executed only 1 time it takes a long time.
SELECT `date` as dateTo
FROM table_name tbl
WHERE `colA` = 223 and `colB` <> 1
ORDER BY `date` DESC
LIMIT 1
The database table has about 2 million records and the ORDER BY is slowing the execution time.
What is the best INDEX I could have in this scenario?
Would an index on date only be beneficial or would I have to include colA and colB?
-----
I ended up using this query,
SELECT `ColA`,`date`, `ColB`
FROM atm_status_log
WHERE `ColA` = 223
HAVING `ColB` <> 1
ORDER BY `date` DESC
LIMIT 1;
and this INDEX, INDEX(colA, colB, date)
You have to add a index on date, colA, colB.
Please keep in mind the order off that index matters.
ALTER TABLE table_name ADD KEY index_name (date, colA, colB);
You have to add index for colA and colB. That will resolve the issue.
This is your query:
SELECT `date` as dateTo
FROM table_name tbl
WHERE `colA` = 223 and `colB` <> 1
ORDER BY `date` DESC
LIMIT 1
One approach to indexing would be table_name(colA, colB, date). This is a covering index for the query. However, it will not eliminate the sorting.
You could try this approach:
SELECT dateTo
FROM (SELECT `date` as dateTo, colB
FROM table_name tbl
WHERE `colA` = 223
ORDER BY `date` DESC
) t
WHERE `colB` <> 1;
The subquery should be able to make use of a query on table_name(colA, date). It will need to materialize the entire result set, but then choosing colB <> 1 should be pretty fast. I'm not thrilled with this approach, because it assumes that the subquery remains ordered when read by the outer query -- but that is true in MySQL.
How many different value in colB? If it can have only 0 and 1, then change the filter to
colB = 0
and add this
INDEX(colA, colB, date)
(The date must be last, but A and B can be in either order.) Only then can the all the filtering and the ORDER BY be handled by the INDEX.
If colB has more than 2 values, then let's slightly improve on Gordon's solution:
SELECT `date` as dateTo
FROM table_name tbl
WHERE `colA` = 223
AND colB <> 1
ORDER BY `date` DESC;
with INDEX(colA, colB, date), with the columns in exactly that order. This index would be a "covering" index.

Mysql query to count how many "1" or "2"s in the whole table?

I have a phpmyadmin console Running a voting database where users vote for person1 or person2. the entries then get put into a table with userID, and choiceID. user column is auto incrementing and choiceID is either 1 or 2 depending on what they voted for. this is all new to me and I am surprised I have made it this far. could anybody explain how to make a query that counts how many 1,s , and how many 2,s are inside my whole table at any given time? that would be very awesome.
SELECT choiceID, COUNT(userID) as number_of_votes FROM votes GROUP BY choiceID ORDER BY number_of_votes
Given a set of data like this:
Will result in this:
Here the SQL-Command. You just have to replace the tablename.
SELECT COUNT(*), choiceID FROM tablename GROUP BY choiceID;
You can use "CASE WHEN" clauses in your query:
SELECT
SUM( CASE WHEN choiceID = 1 THEN 1 ELSE 0 END ) AS choice_1,
SUM( CASE WHEN choiceID = 2 THEN 1 ELSE 0 END ) AS choice_2
FROM ...
WHERE ...
select count(*) from -tablename- where choiceID = '1'
or '2'...depending what you want :P

how should i do this query in mysql

I want to do the following. I have a table in the database, I am working on a table called asistencia and this table has 3 columns
id_asistencia as a int AUTOINCREMENT
nro_matricula as an int which I took it from another table called
alumnos
fecha as a date
This is a sketch of the database
id_asistencia | nro_matricula | fecha
1 | 0001| 2015-01-10
2 | 0002| 2015-01-10
3 | 0002| 2015-02-10 (another date )
The thing is I have to do a percentage
select all id_1 records in my nro_matricula column and see how many times its repeated in my rows and do a percentage respect all the dates in my database
EG : id_1 came to class day(whatever day) and he/she did not came to class the next day so id_1 has 50% assistance
Expected result
nro_matricula | percentage
0001| 50
0002| 100
The question is how can I make this query. If can be done in PHP its even better but i feel that this can be done in SQL
PS : The Database wasn't created by me
And excuse my English is not the better and i expect it to be understandable for you to help me
You can use sql statement like this:
select (
sum (if nro_matricula = '001' ,1,0 )
/ count(*)
from asistencia
--where nro_matricula = '001'
Maybe just simply:
select al.nro_matricula,
100 * count(distinct al.fecha) / (select count(distinct al1.fecha) from alumnos al1) as percentage
from alumnos al
group by al.nro_matricula
I did found the answer to my question. Thank you all for helping me out
SELECT
asistencia.nro_matricula as matricula,
COUNT( DISTINCT asistencia.fecha)* 100 /
COUNT( DISTINCT asistencia.nro_matricula) / (SELECT COUNT(DISTINCT asistencia.fecha)
FROM asistencia
ORDER BY COUNT(*) DESC
LIMIT 1 )
as porcentaje_asistencia
FROM asistencia
JOIN alumno
WHERE asistencia.nro_matricula = alumno.nro_matricula AND alumno.id_curso = 'basica6a'
Tried this in Oracle. Should work in MySQL too.
SELECT aa.NRO_MATRICULA , days_present/total_count* 100 FROM
(SELECT DISTINCT NRO_MATRICULA,
COUNT(*) as days_present FROM ASISTENCIA GROUP BY NRO_MATRICULA ) AA
,
(SELECT COUNT(*) as total_count FROM (SELECT DISTINCT(FECHA) FROM ASISTENCIA GROUP BY FECHA)) BB
Ouptut
nro_matricula percentage
0001 50
0002 100
The query (SELECT COUNT(*) FROM (SELECT DISTINCT(FECHA) FROM ASISTENCIA AA GROUP BY FECHA)) will give count of distinct date (2 in your case). Then we are getting distinct nro_matricula group by nro_matricula to get its count which will give the days it was present. Then divide both values from above steps to get percentage.

Inserting the results of a mysql query into existing table

I have a an existing table with timestamps and other values and I wanted to create dummy timestamps for each day in the table
For example
timestamp phase_1 phase_2 phase_3
2014-03-04 12:00:00 0 0 0
2014-03-05 02:00:00 0 0 0
2014=03-06 01:00:00 0 0 0
2014-03-07 00:00:00 0 3 1
should result to
timestamp phase_1 phase_2 phase_3
2014-03-04 00:00:00 0 0 0 --<< new
2014-03-04 12:00:00 0 0 0
2014-03-05 00:00:00 0 0 0 --<< new
2014-03-05 02:00:00 0 0 0
2014-03-06 00:00:00 0 0 0 --<< new
2014-03-06 01:00:00 0 0 0
2014-03-07 00:00:00 0 3 1
The following query works fine
`select * from Table1
right join
(select
`timestamp`
, phase_1
, phase_2
, phase_3
from Table1
union
select
date(`timestamp`)
, 0
, 1
, 2
from Table1
order by
`timestamp`) `
but I am not able to insert the result into the exisiting database
using the following query
`INSERT into Table1
select * from Table1
right join
(select
`timestamp`
, phase_1
, phase_2
, phase_3
from Table1
union
select
date(`timestamp`)
, 0
, 1
, 2
from Table1
order by
`timestamp`) `
Which i think is correct but i get a
: Every derived table must have its own alias: error which i am not sure on how to solve
Here is the fiddle enter link description here
Also, will it be possible to insert dummy timestamp and values for days which are not there at all in the table?
Suppose there are two days between the first and last rows in the table and there are no timestamps for those days in the table, will it be possible to add those two dates into the table.
I want to avoid trigger and procedures if possible as I am not at all familiar with them and since the table is dynamically generated, i think it wont be possible to use them.
You've got to use an alias name for your UNION, but you've got to provide an JOIN condition for your right join too. Probably you want to use:
INSERT INTO Table1
select a.* from Table1
right join
(
select
`timestamp`
, phase_1
, phase_2
, phase_3
from Table1
union
select
date(`timestamp`)
, 0
, 1
, 2
from Table1
order by
`timestamp`
) as a
ON DATE(a.timestamp) = DATE(Table1.timestamp)
Remarks:
What is the difference between Left, Right, Outer and Inner Joins? provides a very good explanation of the different joins.
An outer join needs a join condition, an inner join without a condition is the same as an CROSS JOIN, the cartesian product of both tables (most times one wants to avoid this - but not ever).
If you use a SELECT statement, that works perfectly as SELECT statement alone in a join, then the result of this SELECT statement changes into a derived table and you've got to give this derived table a name so you can use the columns of the derived table.
I hope it will be a little bit more clear now.
A better (more efficient) way to write this query is to remove the union and use union all and to remove the join. The basic query you want seems to be:
select `timestamp`, phase_1, phase_2, phase_3
from Table1
union all
select date(`timestamp`), 0, 1, 2
from Table1 t1
group by date(`timestamp`)
having not exists (select 1 from table1 tt1 where tt1.timestamp = date(t1.timestamp))
order by `timestamp`;
The group by and having clauses in the second query prevent duplicates, if the value already appears in the table. Honestly, though, it is probably better to have a unique constraint on timestamp.
If you want to insert rows into the table, you only need the second portion of the query:
INSERT INTO Table1(timestamp, phase_1, phase_2, phase_3)
select distinct date(`timestamp`), 0, 1, 2
from Table1
group by date(`timestamp`)
having not exists (select 1 from table1 tt1 where tt1.timestamp = date(t1.timestamp));
The original data is already there.
To insert a "dummy" timestamp for days not in the table, you need a way to generate rows with those values. This can be a pain in MySQL. But, if you have a calendar table that has all the dates, you can do something like:
INSERT INTO Table1(timestamp, phase_1, phase_2, phase_3)
select c.dte, 0, 1, 2
from calendar c join
(select min(date(timestamp)) as mind, max(date(timestmp)) as maxd
from Table1
) tmm
on c.dte between tmm.mind and tmm.maxd and tmm.mind left join
Table1 t1
on date(t1.timestamp) = c.dte
where t1.timestamp is null;

Mysql error in a count query which counts a union subquery

Can you please check this mysql query and maybe help with what's wrong with it?
When I run it through mysql as my host i get this error:
#1248 - Every derived table must have its own alias
Here is the code:
mysql_query("
SELECT COUNT(*)
FROM
(
(SELECT 1 as sort_col,id,pic0 FROM `cronjob_reloaded` WHERE id IS NOT NULL AND id LIKE '%car%')
UNION
(SELECT 2 as sort_col,id,pic0 FROM `cronjob_reloaded` WHERE id IS NOT NULL AND category IN ('bmw'))
ORDER BY sort_col
)
")
Ty!
PS. I have posted an unclear question some time ago, can a admin please delete that one? And sorry for any inconvenience. The question is here
As the error says, derived tables must be aliased.
SELECT COUNT(*)
FROM
(
(SELECT 1 as sort_col,id,pic0 FROM `cronjob_reloaded` WHERE id IS NOT NULL AND id LIKE '%car%')
UNION
(SELECT 2 as sort_col,id,pic0 FROM `cronjob_reloaded` WHERE id IS NOT NULL AND category IN ('bmw'))
ORDER BY sort_col
) q /* I added the alias "q" */

Categories