PHP mysql Distinct, only load 1 set of ids - php

I'm trying to get data where order doesn't matter with unique ids. So simply my query would be
SELECT DISTINCT id1, id2 FROM messages ORDER BY date
If i have a database with the following data:
id1 | id2 | date
5 | 6 | 1/2/2011
6 | 5 | 1/1/2011
I would only need to load the column with the newest date because the ids are the same 2 people. Really i have to load ids where one of the ids is yours so my real query right now is
SELECT DISTINCT userid_1, userid_2
FROM messages
WHERE userid_2=$dbid
OR userid_1=$dbid
ORDER BY date
and i get a result as [6 5] [5 9] [9 5] [5 15] [5 6] [5 17]
Results 2 and 3 are the same and 1 and 5 are the same. Really only 4 results should be queried. Thanks!

One option is:
SELECT DISTINCT
if(userid_1 >= userid_2,userid_1, userid_2) AS 'id1',
if(userid_1 >= userid_2,userid_2, userid_1) AS 'id2'
FROM messages
WHERE userid_2=$dbid OR userid_1=$dbid ORDER BY date
This query shows two fields for each record.
The first field will be userid1 if it is bigger or equal to userid2, else will show userid2
The second field has the opposite logic
This makes sure that two similar sets of results will alwas be ordered the same way, so the DISTINCT will regard it as same.

If your goal is to get distinct pairings, you could do the following to get the smaller of the pair always in the left column and the larger into the right, thus ensuring that distinct works:
SELECT DISTINCT
(id1 - ( (id1 - id2) + abs(id1 - id2) ) * .5) as first,
(id2 + ( (id1 - id2) + abs(id1 - id2) ) * .5) as second
FROM messages ORDER BY date;
Since the pairs are always arranged, no need for any pairing functions.
Example
+------+------+--------+------------+
| id1 | id2 | val | pair_date |
+------+------+--------+------------+
| 4 | 5 | test 1 | 2010-12-25 |
| 5 | 4 | test 2 | 2011-10-31 |
| 17 | 50 | test 3 | 2011-07-04 |
| 50 | 17 | test 4 | 2001-01-01 |
+------+------+--------+------------+
If I run this query:
SELECT DISTINCT
(id1 - ( (id1 - id2) + abs(id1 - id2) ) * .5) AS first,
(id2 + ( (id1 - id2) + abs(id1 - id2) ) * .5) AS second
FROM pairs ORDER BY pair_date;
I get:
+-------+--------+
| first | second |
+-------+--------+
| 4.0 | 5.0 |
| 17.0 | 50.0 |
+-------+--------+
2 rows in set (0.00 sec)
Obviously using a boolean has the same effect, is easier to read, and probably is faster, but the above algorithm is handy if you just need to swap two numbers so that one side is always the lesser.
I'm not sure why you would have gotten an error. I'm running my tests directly from the MySQL command line....

Related

How to GROUP GROUP_CONCAT()?

I am looking for a way to get groups of the GROUP_CONCAT() function in a single query, for example.
My current code
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(service_info.ip_address SEPARATOR ','),',',service_plans.aggregation) AS ip_address
FROM
services
LEFT JOIN
service_info
ON
service_info.service_id = services.id
LEFT JOIN
service_plans
ON
service_plans.id = services.service_plan_id
WHERE
service_plans.id = '2'
I want to group the IP addresses by a specific number(the $group_by variable if you see in the query) but then separate by a different character such as ":" or something.
Essentially I want my output to look like:
If $group_by=2: 10.1.1.2,10.1.1.3:10.1.1.4,10.1.1.5
If $group_by=3: 10.1.1.2,10.1.1.3,10.1.1.4:10.1.1.5
Is this possible to implement into my current query?
UPDATE: table structure
Table service_plans
id | name | aggregation
-----------------------------------------
1 | Uncapped 10Mbps 20:1 | 20
2 | Uncapped 20Mbps 10:1 | 10
3 | Capped 30Mbps | 0
Table services
id | service_plan_id | description
------------------------------------
1 | 2 | Phone
2 | 2 | Laptop
3 | 2 | PC
4 | 2 | TV
5 | 2 | Test
Table service_info
id | service_id | ip_address
------------------------------
1 | 1 | 10.1.1.2
2 | 2 | 10.1.1.3
3 | 3 | 10.1.1.4
4 | 4 | 10.1.1.5
5 | 5 | 10.1.1.6
I am trying to get an array of ip_address's concatenated and separated by a comma but the in groups of however much the service_plans.aggregation value is.
If aggregation is 2, then my output should be:
10.1.1.2,10.1.1.3:10.1.1.4,10.1.1.5
As you can see they are in groups of 2 and then the next group is separated by a colon(:)
If aggregation is 3, then my output should be:
10.1.1.2,10.1.1.3,10.1.1.4:10.1.1.5
As you can see they are in groups of 3 and then the next group is separated by a colon(:) and so on
Your post is a little confusing. What would be helpful is if you posted sample data, and then posted what you want your query to return. I'll give you an answer to what I think you're asking, based on the subject of your post.
ServicePlanIPs
service_plan_id | ip_address
-------------------------------
1 | 192.168.70.1
1 | 192.168.70.2
1 | 192.168.70.3
2 | 192.168.70.4
2 | 192.168.70.5
2 | 192.168.70.6
If you run this query against ServicePlanIPs:
SELECT service_plan_id, GROUP_CONCAT(ip_address) as ip_addresses
FROM ServicePlanIPs
GROUP BY service_plan_id
You will get:
service_plan_id | ip_addresses
-------------------------------
1 | 192.168.70.1, 192.168.70.2, 192.168.70.3
2 | 192.168.70.4, 192.168.70.5, 192.168.70.6
I don't guarantee this will run out of the box, but it should get you on the right track. Hope it helps. Note - if you're using a version of mysql which supports window functions, you can do something similar to the below and use the natively supported RANK function instead of doing it manually with variables.
SET #curRank := 0;
SET #concatIps := '';
SELECT
sp.id,
#curRank := #curRank + 1 AS rank,
IF(MOD(#curRank, (SELECT aggregation FROM service_plans WHERE id = {service_plan_id}) = 0, #concatIps := CONCAT(#concatIps, ':', s.ip_address), #concatIps := CONCAT(#concatIps, ',', s.ip_address))
FROM service_plans sp
JOIN services s
ON sp.id = s.service_plan_id
JOIN service_info si
ON si.service_id = s.id
WHERE sp.id = {service_plan_id}
ORDER BY service_info_id

DISTINCT * is not withdrawing the duplicity (MYSQL)

i already done everything to remove this duplicity on the database
On selecting a checkbox on the sectio "Bairros" i utilized as Array
for($m=0; $m<count($_POST["bairros"]); $m++){// LOOP 1
$pesquisar=($_POST["bairros"][$m]);
//Copy bairros(Array) and esporte (POST)
$query = "SELECT DISTINCT * FROM cadastro WHERE
(esporte1 = '".$_POST["esportes"]."' OR
esporte2 = '".$_POST["esportes"]."' OR
esporte3 = '".$_POST["esportes"]."' OR
esporte4 = '".$_POST["esportes"]."')
AND
(bairro1 = '".$pesquisar."' OR
bairro2 = '".$pesquisar."' OR
bairro3 = '".$pesquisar."' OR
bairro4 = '".$pesquisar."')
AND
ativarAparecer='sim' ORDER BY nomeCompleto ASC LIMIT 20";
$esporte= new consultar();
$esporte->executa($query);
//Loops
for($l=0; $l<$esporte->nrw; $l++){ //LOOP 2
echo $esporte->data["nomeCompleto"]."<br />";
$esporte->proximo();
} //close LOOP2
} //close LOOP1
Detail: this function object oriented, I believe that i'm doing something wrong at SQL or MYSQL, perhaps something is missing there.
SELECT DISTINCT *
Stop There. DISTINCT * can do what? Duplicate of what? it cant do that. Give it a field name to see unique values.
For example
SELECT DISTINCT nomeCompleto
Let's break this down. The DISTINCT clause will return unique sets based on the selected columns.
Let's say you have a table:
a | b | c
=========
1 | 2 | 3
1 | 1 | 3
1 | 2 | 4
Now if you SELECT DISTINCT a FROM table, you would get:
1
but if you SELECT DISTINCT a, b FROM table, you would get:
a | b
=====
1 | 2
1 | 1
That's because {1,2} is different from {1,1}, even though the a column is the same between those two sets.
Obviously, doing SELECT DISTINCT * FROM table would give you the original table because it uses all three columns as a "composition" of the unique set. If we amended the table to look like this:
a | b | c
=========
1 | 2 | 3
1 | 1 | 3
1 | 2 | 4
1 | 2 | 3
Then your result of SELECT DISTINCT * FROM table would give:
a | b | c
=========
1 | 2 | 3
1 | 1 | 3
1 | 2 | 4
because of the duplicate result set of {1, 2, 3}. However, since most tables have an auto-incrementing identifier as the primary key, there is almost always no difference between SELECT * and SELECT DISTINCT *.
Perhaps you're looking to GROUP BY a certain column?
How would I be using GROUP this in my script? Column that there are several equal records are this bairro, bairro2, bairro3, bairro4. Inside it is in numbers
bairro1 | bairro2 | bairro3 | bairro4
14 | 14 | 15 | 27
34 | 15 | 14 | 30
27 | 45 | 12 | 14

How to get unique result from below entry?

plan_id | elementclass | table_no | ress_id | UserID | Status
1 | elementclass1 | 1 | 0 | 0006100022 | N
1 | elementclass1 | 1 | 2 | 0006100022 | N
1 | elementclass2 | 2 | 0 | 0006100021 | N
1 | elementclass4 | 3 | 0 | 0006100023 | N
in above row I am expecting as this
if row is having same elementclass,table_no but different ress_id in that condition only take that row which is non zero.If with above condition tow rows having 0 it can take any row .if both rows have non zero then also it can take any one.
Now
for rest of others it can take values with 0.We can use group by to plan_id as there may be multiple plans.
Desired result
plan_id | elementclass | table_no | ress_id | UserID | Status
1 | elementclass1 | 1 | 2 | 0006100022 | N
1 | elementclass2 | 2 | 0 | 0006100021 | N
1 | elementclass4 | 3 | 0 | 0006100023 | N
Please help.
thanks
SELECT * FROM TableName a
WHERE a.ress_id = (SELECT MAX(b.ress_id) FROM TableName b WHERE b.table_no = a.table_no)
GROUP BY a.plan_id,a.table_no
This gives you:
1 result per plai_id and table_no
each result has biggest ress_id in it
First get the maximum ress id per element class. Then select the related records. There may be duplicates. Hence group by element class and ress id.
The following statement does not precisely do what you asked for, but maybe it suffices. In case of a tie you won't get one of the records, but one of the records' plan ids, one of the records' table nos, one of the records' user ids and one of the records' statusses. So the user id may be taken from one record and the status from another when elementclass and ress_id are equal.
select plan_id, mytable.elementclass, table_no, mytable.ress_id, userid, status
from mytable
join
(
select elementclass, max(ress_id) as max_ress_id
from mytable
group by elementclass
) agg on agg.elementclass = mytable.elementclass and agg.max_ress_id = mytable.res_id
group by mytable.elementclass, mytable.ress_id;
(It is possible to write a statement to access complete records in case of ties, but this is much more complicated - at least in MySQL.)
Try this:
SELECT T1.*
FROM TableName T1 JOIN
(SELECT elementclass,table_no,MAX(ress_id) as ress_id
FROM TableName
GROUP BY elementclass,table_no
)T2 ON T1.elementclass=T2.elementclass AND T1.table_no=T2.table_no AND T1.ress_id=T2.ress_id
Explanation:
Here, we are creating a temporary table T2 with maximum of ress_id for each elementclass and table_no. Then we join this table with the original table with these 3 fields and select all records from the original table T1.
Result:
PLAN_ID ELEMENTCLASS TABLE_NO RESS_ID USERID STATUS
-------------------------------------------------------------------
1 elementclass1 1 2 0006100022 N
1 elementclass2 2 0 0006100021 N
1 elementclass4 3 0 0006100023 N
See result in SQL Fiddle.

MySQL: Standard deviation average within a table

Here's how one of my tables are structured:
id | group | val1 | val2 | val3
1 | 1 | 22 | 23 | 60
2 | 1 | 40 | 60 | 80
3 | 2 | 50 | 5 | 70
4 | 2 | ...
5 | 2 |
6 | 3 |
...
In my PHP-document I'm calculating the standard deviation by using val1+val2+val3 etc. per row WHERE group equals the one I'm displaying.
Now I want to know, by using MySQL, what the standard deviation is per row and the average across a group. (row1stddev+row2stddev+...)/n
I've tried using subqueries, but all I can achieve is getting a single value. I think I have a lack of understanding how the the built in standard deviation functions in MySQL actually works with multiple values.
EDIT.
This is the two things I'm looking for:
id | group | stddev
1 | 1 | 21,65641
2 | 1 | 20
3 | 2 | 33,29164
4 | 2 | ...
5 | 2 |
6 | 3 |
And average by group (average of all stddev):
group | avg_stddev
1 | 20,828205
2 | ...
3 | ...
The point of this is that I want to know in which group the difference is largest.
Since mysql native function STDDEV_POP accepts only column, you have to trick it by using temporary table, that will have one column with all values (that matters to standard deviation) in a row, for that use UNION ALL with 3 SELECTS, each for one meaningful column, and group it by row id.
SELECT
id,
STDDEV_POP(val) AS row_stddev
FROM (
SELECT id, val1 AS val FROM stdev_table
UNION ALL
SELECT id, val2 AS val FROM stdev_table
UNION ALL
SELECT id, val3 AS val FROM stdev_table
) tmp0
GROUP BY id
you can also select additional column - group and create another temporary table to select average from it:
SELECT
id_group,
AVG(row_stddev) AS group_avg
FROM (
SELECT
id,
id_group,
STDDEV_POP(val) AS row_stddev
FROM (
SELECT id, id_group, val1 AS val FROM stdev_table
UNION ALL
SELECT id, id_group, val2 AS val FROM stdev_table
UNION ALL
SELECT id, id_group, val3 AS val FROM stdev_table
) tmp0
GROUP BY id
) tmp1
GROUP BY id_group
If I understood you correctly you should create View
CREATE VIEW view_name AS SELECT val1, val2, val3, val1 + val2 + val 3 / 3 AS average FROM tablename;
and then just SELECT * FROM view_name to get you values.

mysql speed of query - selecting max value of every 3 rows

I have a table that holds price information. I need to select the max value of every three rows. EXAMPLE:
Table `daily_high`
____ _______
| ID | HIGH |
| 1 | 24.65 |
| 2 | 24.93 |
| 3 | 26.02 |
| 4 | 25.33 |
| 5 | 25.16 |
| 6 | 25.91 |
| 7 | 26.05 |
| 8 | 28.13 |
| 9 | 27.07 |
|____|_______|
Desired output to new table (ID will be auto-increment so don't assume an association exists between this ID 1 and the daily_high ID 1:
____ ___________
| ID | 3MaxHIGH |
|____|___________|
| 1 | 26.02 |
| 2 | 25.91 |
| 3 | 28.13 |
|____|___________|
I want to compare IDs 1,2, and 3 to determine the high value among them. Then once I have compared 1-3, I want to move on to 4 through 6, then 7 through 9, etc until I've done this for all values contained in the table (currently about 400,000 values). I have written code that uses
SELECT max(HIGH) FROM daily_high as dh1 JOIN (SELECT max(HIGH) FROM daily_high WHERE id >= dh1 AND id < (dh1.id + 3))
This works but is horribly slow. I've tried using the SELECT statement where I identify the column values to be pull for display, meaning between the SELECT and FROM parts of the query.
I've tried to use JOIN to join all 3 rows onto the same table for comparison but it too is horribly slow. By slow I mean just under 10 seconds to gather information for 20 rows. This means that the query has analyzed 60 rows (20 groups of 3) in 9.65879893303 seconds (I didn't make this up, I used microtime() to calculate it.
Anyone have any suggestions for faster code than what I've got?
Keep in mind that my actual table is not the same as what I've posted above, but it the concept is the same.
Thanks for any help.
If you ID it continous you can make this
SELECT floor(id/3) as range, max(HIGH) FROM daily_high GROUP BY range;
Why not to use DIV operator for grouping your aggregation:
SELECT (id-1) DIV 3 + 1 AS ID, MAX(high) AS 3MaxHIGH
FROM daily_high
GROUP BY (id-1) DIV 3
This query gives the same result.
ID 3MaxHIGH
1 26.02
2 25.91
3 28.13
I was unable to run your query, and I believe that this one is faster.
UPD: To ensure that you have valid groups for your ranges, use this query:
select id, high, (id-1) div 3 + 1 from daily_high
result:
id high (id-1) div 3 + 1
1 24.65 1
2 24.93 1
3 26.02 1
4 25.33 2
5 25.16 2
6 25.91 2
7 26.05 3
8 28.13 3
9 27.07 3
Fuller answer with an example. The following code will do what I think you want.
SELECT FLOOR((row - 1) / 3), MAX(Sub1.high)
FROM (SELECT #row := #row + 1 as row, daily_high.*
FROM daily_high, (SELECT #row := 0) r) Sub1
GROUP BY FLOOR((row - 1) / 3)
ORDER BY Sub1.ID
The below query worked for me on a test table. perhaps not the best, but the other solutions failed on my test table.
This does require the ID's to be sequential. Also be sure to put an index on High aswell for speed.
SELECT FLOOR(T1.Id/3)+1 AS Id, ROUND(GREATEST(T1.High, T2.High, T3.High),2) AS High FROM `daily_high` T1, `daily_high` T2, `daily_high` T3
WHERE T2.Id=T1.Id+1
AND T3.Id=T2.Id+1
AND MOD(T1.Id, 3)=1
logic: if(id is divisible by 3, id/3-1, id/3)
select if(mod(id,3) = 0,floor(id/3)-1,floor(id/3)) as group_by_col , max(HIGH)
FROM daily_high GROUP BY group_by_col;

Categories