Merge Rows AND/OR Columns into one field in a GROUP BY - php

My SQL table looks like
+-----+------------+--------------+--------------+-----------+------------+-----------+
| rid | ship_to_id | product_code | product_name | first_row | second_row | third_row |
+-----+------------+--------------+--------------+-----------+------------+-----------+
| 1 | 555 | A | Crystal | 1 | 2 | 3 |
| 1 | 555 | A | Crystal | 4 | 5 | 6 |
| 2 | 333 | B | Diamond | first | second | third |
| 2 | 333 | A | Crystal | ROW 1 | ROW 2 | ROW 3 |
| 2 | 333 | A | Crystal | ROW 4 | ROW 5 | ROW 6 |
+-----+------------+--------------+--------------+-----------+------------+-----------+
And I am trying to get following results
+-----+------------+--------------+-------------------------------------+
| rid | ship_to_id | product_name | data |
+-----+------------+--------------+-------------------------------------+
| 1 | 555 | Crystal | 1 2 3 4 5 6 |
| 2 | 333 | Diamond | first second third |
| 2 | 333 | Crystal | ROW 1 ROW 2 ROW 3 ROW 4 ROW 5 ROW 6 |
+-----+------------+--------------+-------------------------------------+
Can someone please tell me what is wrong with my code. Thanks
$query = mysql_query("SELECT * FROM mytable group by rid, ship_to_id, product_code, product_name");
while($row = mysql_fetch_array($query)) {
echo "$row[rid] $row[ship_to_id] $row[product_code] $row[product_name] $row[first_row] $row[second_row] $row[third_row] <br>";
}

You're forgetting product_code sometimes, I guess.
This probably is bad database design. You should learn about Database normalization and related techniques.
Third, SQL normally produces the same number of columns for every result record, so the best result you could have has rows like 1, 555, "A", "Crystal", "1 4 2 5 3 6" (notice these are 5 values). But I cannot think of a concatenation method that would produce the numbers in the 1 2 3 4 5 6 order. Depending on the better form of your database-schema-to-be, this might be something easier done in PHP than SQL.
If you're still up to concatenating all values into a single cell (as in the newly formatted example output), the you should try something along these lines:
SELECT
rid, ship_to_id, product_code, product_name,
GROUP_CONCAT(CONCAT(first_row,'#',second_row,'#',third_row) SEPARATOR '~') AS data
FROM tablename
GROUP BY rid, ship_to_id, product_code, product_name;
Notice that the values of first_row etc. are separated by a '#' and the values from distinct records by a '~' sign. You can find more options in the documentation of MySQL's GROUP_CONCAT().

you can try something like
select rid,ship_to_id,product_name,
GROUP_CONCAT(CONCAT(first_row,' ',second_row,' ',third_row) as new_val
from mytable
group by rid,ship_to_id,product_name;

As you group by the first 4 columns and use no aggregation function on the remaining ones, data will be lost when multiple record have the same values in the columns grouped by.
So basically the result will be like the following:
+-----+------------+--------------+------------------------------------+
| rid | ship_to_id | product_name | first_row | second_row | thrid_row |
+-----+------------+--------------+------------------------------------+
| 1 | 555 | Crystal | 1 | 2 | 3 |
| 2 | 333 | Diamond | first | second | third |
| 2 | 333 | Crystal | ROW 1 | ROW 2 | ROW 3 |
+-----+------------+--------------+------------------------------------+
That's what group by means. To keep the data of the fields containing varying data, you must use an aggregation function on them, like GROUP_CONCAT. Example:
SELECT rid, ship_to_id, product_code, product_name, GROUP_CONCAT(first_row), GROUP_CONCAT(second_row), GROUP_CONCAT(third_row)
FROM mytable
GROUP BY rid, ship_to_id, product_code, product_name
This will concatenate the values of the corresponding record like this:
+-----+------------+--------------+------------------------------------------+
| rid | ship_to_id | product_name | first_row | second_row | thrid_row |
+-----+------------+--------------+------------------------------------------+
| 1 | 555 | Crystal | 1 4 | 2 5 | 3 6 |
| 2 | 333 | Diamond | first | second | third |
| 2 | 333 | Crystal | ROW 1 ROW 4 | ROW 2 ROW 5 | ROW 3 ROW 6 |
+-----+------------+--------------+------------------------------------------+
However, your approach with GROUP BY is probably not be the best for this case. It would worth to think about to just order by the rid, ship_to_id, product_code, product_name columns, and then iterate through them in PHP with the given order and merge the corresponding rows there.
You may also have to reconsider you database design, as a query like this smells about a bad concept :)

Related

MySQL - PHP unique values from rows

I have this table:
table 1
+----+-----------------------+----------+------+-------+
| ID | COUNTRY | QUANTITY | EACH | PRICE |
+----+-----------------------+----------+------+-------+
| 1 | U.S.A | 1 | 12 | 1*12 |
| 2 | U.K. | 2 | 3 | 2* 3 |
| 3 | GERMANY | NULL | 3 | |
| 4 | FRANCE;GERMANY; U.S.A | 0 | 7 | |
| 5 | U.S.A;GERMANY | 3 | 8 | 3*8 |
| 6 | FRANCE;U.K. | 1 | 10 | 1*10 |
| 7 | U.S.A;FRANCE | 2 | 6 | 2*6 |
| 8 | FRANCE;FRANCE | 9 | 3 | 9*3 |
+----+-----------------------+----------+------+-------+
and this code sql:
SELECT
COUNTRY,
SUM(COALESCE(IF(QUANTITY = NULL OR QUANTITY = 0,1,QUANTITY), 1) * EACH) AS PRICE
FROM table1
GROUP BY COUNTRY
How could I make unique values for the country column and return: USA = 48 (ID: 1+5+7); UK= 6; GERMANY=3; FRANCE = 44 (ID: 4+6+8). I want that the rows, those contain two, three, four countries to be eliminated and to remain only the first country from row.
Thank you!
Use substring_index to get the first country in the ;-separated list.
SELECT
SUBSTRING_INDEX(COUNTRY, ';', 1) AS COUNTRY
SUM(IF(QUANTITY IS NULL OR QUANTITY = 0,1,QUANTITY) * EACH) AS PRICE
FROM table1
GROUP BY COUNTRY
It would be much more complicated if you wanted to keep all the rows where each country appears (in that case I would recommend doing it in PHP, not MySQL, since MySQL doesn't have a builtin way to do explode()).

MySQL MIN() limit by column

I have a MySQL table that is formatted as follows:
group_clue:
---------------------------------------------------
| id | group_id | clue_id | completed | run_order |
---------------------------------------------------
| 1 | 1 | 2 | 1 | 1 |
| 2 | 1 | 6 | 1 | 2 |
| 3 | 1 | 4 | 1 | 3 |
| 4 | 1 | 3 | 0 | 4 |
| 5 | 1 | 1 | 0 | 5 |
| 6 | 1 | 5 | 0 | 6 |
| 7 | 2 | 9 | 1 | 1 |
| 8 | 2 | 2 | 0 | 2 |
...
---------------------------------------------------
The data above in the group_clue is constructed such that each group_id has every clue_id at some run_order (ranging from 1 to the number of clue_ids and not repeating for a particular group).
First Question
I want to create a table showing the first clue_id for each group_id where completed = 0 when ordered by run_order (aliased as current_clue). Using the above example, this would give:
---------------------------
| group_id | current_clue |
---------------------------
| 1 | 3 |
| 2 | 2 |
---------------------------
My preliminary attempt is:
SELECT group_id, MIN(clue_id) as current_clue
FROM group_clue
WHERE completed = 0
GROUP BY group_id
However, this returns the same clue_id for each group_id.
Second Question
From the data in the first question, I would like to compose a final table where I GROUP_CONCAT() these results so that it contains every current_clue and each group_id that contains that current_clue. I would also like it ordered from those clues with the most group_ids to those with the fewest. An example resulting table is:
--------------------
| clue | group_ids |
--------------------
| 3 | 1,5,4,3 |
| 2 | 2,6 |
--------------------
I cannot figure out the ordering. My preliminary attempt is:
SELECT clue_id, GROUP_CONCAT(group_id)
FROM [resulting_table]
GROUP BY clue_id
ORDER BY [something]
Any help is appreciated: what queries would fit this scenario?
The first part of your question can be solved this way (it expects that run_order is unique per group):
SELECT t1.group_id,
t1.clue_id AS current_clue
FROM group_clue t1
INNER JOIN (SELECT group_id,
MIN(run_order) as run_order
FROM group_clue
WHERE completed = 0
GROUP BY group_id) t2 USING (group_id, run_order)
The logic of this query is pretty simple:
The inner query selects the pairs of group_id and the corresponding minimal value of run_order which has the completed = 0.
After that we join the original table to this set of pairs so that we could select the corresponding clue_id additionally.
You can sort by number of elements per group using
ORDER BY COUNT(*) DESC

Multiple select and condition by column

I am trying to figure out how to select an item from table by his column name p.e.
strucutre looks like this
table items
id | name | column_1 | column_2 | column_3 |
1 | nm1 | 1 | 4 | 7,8 |
2 | nm2 | 2,3 | 4 | 9 |
3 | nm3 | 3,1 | 4 | 7 |
table columns
id | c_name |
1 | cnm1 |
2 | cnm2 |
3 | cnm3 |
table column_values
id | c_id | value |
1 | 1 | abcd |
2 | 1 | cdbh |
3 | 1 | dsff |
4 | 2 | wewe |
5 | 2 | cgbh |
6 | 2 | cdlh |
7 | 3 | adbh |
8 | 3 | qdbh |
9 | 3 | pdbh |
So when I wanted to find "abcd" I tried
"SELECT a.* FROM items a, columns b, column_values c WHERE c.`value` LIKE '%abcd%' GROUP BY a.`id`"
but I knew this will find nothing without any conections so i went further
"SELECT a.* FROM items a, columns b, column_values c WHERE c.`value` LIKE '%abcd%' AND b.`id` = c.`c_id` GROUP BY a.`id`"
still no proper connection with item
and here is the problem there is a changing amount of columns and the name contains id of the column and value is set of ids of column values.
So i need to select item by his name which is "name+"column_id in column_values by FIND IN SET cause if i look for abcd it is id 1 and there are two items which have id 1 in column_1
item 1 and item 2
ALSO id of values are AI so there is not possibility of duplicate so there is no need to check of column_id to search in proper column in item table
it can work something like for from 0 - 30 p.e. and it will search in column_1, column_2 column_3 until 30 for id match

Getting duplicate count and data from table

I have 3 tables like this:
table_1
+-ID-+-table_3_id-+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
+----+------------+
table_2
+-ID-+-table_1_id-+-name--+-value-+
| 1 | 1 | Name1 | Data1 |
| 2 | 1 | Name2 | Data2 |
| 3 | 1 | Name3 | Data3 |
| 4 | 2 | Name1 | Data1 |
| 5 | 2 | Name2 | Data4 |
| 6 | 2 | Name3 | Data5 |
| 7 | 3 | Name1 | Data6 |
| 8 | 3 | Name2 | Data2 |
+----+------------+-------+-------+
table 3 consists of IDs and other data that is irrelevant to this question. However, I need to be able to filter on table_3_id.
This is what I need:
Table 2 has multiple rows that have information of the rows in table_1. I need to have a query that checks if there are duplicates in the 'data' column, which have the same 'name'. The result I need would be this (with a WHERE table_3_id = 1):
+-table_3_id-+-name--+-value-+-duplicate-+
| 1 | Name1 | Data1 | true |
| 1 | Name2 | Data2 | false |
| 1 | Name3 | Data3 | false |
+------------+-------+-------+-----------+
Or, if possible, only return the data from table_2 where it actually is a duplicate. The 'duplicate' field can be a count too, since I do know the amount of rows that have the same table_3_id.
I hope I have made my question clear enough. If it is not clear enough I will try to improve it. I have tried it with joins and subqueries, but my knowledge of SQL isn't enough to make an advanced query like this. I'd prefer this in a single query instead of multiple in PHP.
You can use something along the lines of the below:
select Name
from table
group by name
having count(*) > 1
You need to check for every row in Table_2, if there are duplicates. This can be done with a subquery, like this:
SELECT
B.Table_3_ID
,A.Name
,A.Value
,(SELECT COUNT(*) FROM Table_2 C
WHERE C.Name = A.Name AND C.Value = A.Value)
AS DuplicateCount
FROM Table_2 A
INNER JOIN Table_1 B
ON A.Table_1.ID = B.ID
There may be some errors in my SQL, especially the table aliases, because I have no syntax check at the moment, but the principle should work.

Check for mysql ascending field

I have a field in my mysql database called 'rank'.
I want to run a simple as possible query or php loop that checks if there's an increment starting from 1 in this field across all rows. Doesn't matter the order of the rows, just as long as there's a row with 'rank' of 1, another of 2, another of 3, etc.
I have some code to run if there's a break in the sequence so Im just looking for help on the most resource friendly way of checking.
There needs to be a row with a rank of 1 also.
This will return a row with 0 as the missingrank if rank 1 exists, and any rows in addition to missingrank 0 are missing ranks.
SELECT T.ID - 1 AS [MISSINGRANK]
FROM URTABLE T
LEFT JOIN URTABLE T2 ON T.rank = T2.rank + 1
WHERE T2.ID IS NULL
take a look to this:
mysql> set #a:=1;
mysql> select actor_id,if(#a<>actor_id,'break',#a),#a:=#a+1 from t2 order by actor_id;
+----------+-----------------------------+----------+
| actor_id | if(#a<>actor_id,'break',#a) | #a:=#a+1 |
+----------+-----------------------------+----------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 3 | 4 |
| 4 | 4 | 5 |
| 5 | 5 | 6 |
| 6 | 6 | 7 |
| 7 | 7 | 8 |
.....
| 99 | 99 | 100 |
| 101 | break | 101 |
| 102 | break | 102 |
| 103 | break | 103 |
| 104 | break | 104 |
| 105 | break | 105 |
+----------+-----------------------------+----------+
207 rows in set (0.00 sec)
I don't know if it's what you want to (I tried in local host), where breaks mean that the actor_id has broken the auto_auto_increment, you can add group by , order by, etc, whatever you need

Categories