I'm having trouble using GROUP_CONCAT. I'm pretty sure this is the only way to get what I want but it doesn't seem give me the results I need.
Here is my statement:
SELECT
b.*,
GROUP_CONCAT(c.finance_code) AS finance_codes
FROM
`oc_finance_breakpoints` b
LEFT JOIN
`oc_finance_breakpoints_codes` c ON c.breakpoint_id = b.breakpoint_id;
This will gather data in the finance_breakpoints table, structure below:
breakpoint_id
from_value
to_value
minimum_deposit
As well as multiple "finance codes" from my join table, finance_breakpoint_codes:
breakpoint_code_id
breakpoint_id
finance_code
There can be, are are likely to be, several finance codes to a breakpoint. When I run the sql when there is only one entry, I get the following:
1 | 280.00 | 750.00 | 10 | ONIF6,ONIF10,ONIF12
But if there are two entries in the breakpoints table, all that happens is it tacks the additional finance codes onto the end of the above, meaning I only ever get one row with the first set of data, and all the finance codes in one column.
Ideally I'd like it to return something such as this:
1 | 280.00 | 750.00 | 10 | ONIF6,ONIF10,ONIF12
2 | 750.00 | 1500.00 | 10 | ONIB12-9.9,ONIB24-9.9,ONIB36-9
Rather than:
1 | 280.00 | 750.00 | 10 | ONIF6,ONIF10,ONIF12,ONIB12-9.9,ONIB24-9.9,ONIB36-9
Is there any way of achieving what I want? Am I maybe using the wrong function?
The use of an aggregate function (such as GROUP_CONCAT) in your query ensures that it will return aggregated results, while the absence of an explicit grouping ensures that it will return a single, overall summary row.
You need to add a group by clause to the end of your query - like so:
SELECT
b.*,
GROUP_CONCAT(c.finance_code) AS finance_codes
FROM
`oc_finance_breakpoints` b
LEFT JOIN `oc_finance_breakpoints_codes` c
ON c.breakpoint_id = b.breakpoint_id
GROUP BY b.breakpoint_id
Related
id | segment1 |segment2|segment3|segment4|**FREQUENT**
1 | A | B | A | A | A
2 | B | C | C | C | C
Need to find the most frequent letters from segment1 |segment2|segment3|segment4| i.e to find column FREQUENT.
First of all you need to copy all the segments into a single column. One can do it using UNION or a temporary table.
Then you need to count the frequency grouping the result by ID.
Then you need to get the most frequent value in every group. It can be done using self joining over LEFT JOIN or using ordering and row numbering by group. It is the most complicated part. See
Row number per group in mysql
Then you filter by the row number and enjoy the result.
I am trying to select columns from 2 tables,
The INNER JOIN conditions are $table1.idaction_url=$table2.idaction AND $table1.idaction_name=$table2.idaction.
However, From the query below, there is no output. It seems like the INNER JOIN can only take 1 condition. If I put AND to include both conditions as shown in the query below, there wont be any output. Please look at the picture below. Please advice.
$mysql=("SELECT conv(hex($table1.idvisitor), 16, 10) as visitorId,
$table1.server_time, $table1.idaction_url,
$table1.time_spent_ref_action,$table2.name,
$table2.type, $table1.idaction_name, $table2.idaction
FROM $table1
INNER JOIN $table2
ON $table1.idaction_url=$table2.idaction
AND $table1.idaction_name=$table2.idaction
WHERE conv(hex(idvisitor), 16, 10)='".$id."'
ORDER BY server_time DESC");
Short answer:
You need to use two separate inner joins, not only a single join.
E.g.
SELECT `actionurls`.`name` AS `actionUrl`, `actionnames`.`name` AS `actionName`
FROM `table1`
INNER JOIN `table2` AS `actionurls` ON `table1`.`idaction_url` = `actionurls`.`idaction`
INNER JOIN `table2` AS `actionnames` ON `table1`.`idaction_name` = `actionurls`.`idaction`
(Modify this query with any additional fields you want to select).
In depth: INNER JOIN, when done on a value unique to the second table (the table joined to the first in this operation) will only ever fetch one row. What you want to do is fetch data from the other table twice, into the same row, reading the select part of the statement.
INNER JOIN table2 ON [comparison] will, for each row selected from table1, grab any rows from table2 for which [comparison] is TRUE, then copy the row from table1 N times, where N is the amount of rows found in table2. If N = 0, then the row is skipped. In our case N=1 so INNER JOIN of idaction_name in table1 to idaction in table2 for example will allow you to select all the action names.
In order to get the action urls as well we have to INNER JOIN a second time. Now you can't join the same table twice normally, as SQL won't know which of the two joined tables is meant when you type table2.name in the first part of your query. This would be ambiguous if both had the same name. There's a solution for this, table aliases.
The output (of my answer above) is going to be something like:
+-----+------------------------+-------------------------+
| Row | actionUrl | actionName |
+-----+------------------------+-------------------------+
| 1 | unx.co.jp/ | UNIX | Kumamoto Home |
| 2 | unx.co.jp/profile.html | UNIX | Kumamoto Profile |
| ... | ... | ... |
+-----+------------------------+-------------------------+
While if you used only a single join, you would get this kind of output (using OR):
+-----+-------------------------+
| Row | actionUrl |
+-----+-------------------------+
| 1 | unx.co.jp/ |
| 2 | UNIX | Kumamoto Home |
| 3 | unx.co.jp/profile.html |
| 4 | UNIX | Kumamoto Profile |
| ... | ... |
+-----+-------------------------+
Using AND and a single join, you only get output if idaction_name == idaction_url is TRUE. This is not the case, so there's no output.
If you want to know more about how to use JOINS, consult the manual about them.
Sidenote
Also, I can't help but notice you're using variables (e.g. $table1) that store the names of the tables. Do you make sure that those values do not contain user input? And, if they do, do you at least whitelist a list of tables that users can access? You may have some security issues with this.
INNER JOIN does not put any restriction on number of conditions it can have.
The zero resultant rows means, there is no rows satisfying the two conditions simultaneously.
Make sure you are joining using correct columns. Try going step by step to identify from where the data is lost
I have two mysql tables like bellow:
table_category
-----------------
id | name | type
1 | A | Cloth
2 | B | Fashion
3 | C | Electronics
4 | D | Electronics
table_product
------------------
id | cat_cloth | cat_fashion | cat_electronics
1 | 1 | 2 | 3
1 | NULL | 2 | 4
Here cat_cloth, cat_fashion, cat_electronics is ID from table_category
It is better to have another table for category type but I need a quick solution for now.
I want to get list of categories with total number of products. I wrote following query:
SELECT table_category.*, table_product.id, COUNT(table_product.id) as count
FROM table_category
LEFT JOIN table_product` ON table_category.id = table_product.cat_cloth
OR table_category.id = table_product.cat_fashion
OR table_category.id = table_product.cat_electronis
GROUP BY table_product.id
ORDER BY table_product.id ASC
Question: The sql I wrote it works but I have more then 14K categories and 50K products and the sql works very slow. I added index for cat_* ids but no improvement. My question how can I optimize this query?
I found the query takes 3-4 minutes to process the volume of data I mentioned. I want to reduce the execution time.
Best Regards
As far as I can say every "OR" either in "ON" or "WHERE" part is very cost expensive. It will sound very stupid but I would recommend you to make 3 separate small selects combined together with UNION ALL.
This we do with similar problems both in mysql and postgresql and in some cases when we got "resources exceeded" we had to do it also for bigquery. So it is very stupid and you will have more work but it certainly works and it is much quicker in producing results then many "OR"s.
I need help with a raw SQL query which gets a value based on another value.
I have the following raw SQL query.
SELECT pmPropDef.id, pmPropDef.name, pmPropDef.units, pmPropShort.str, pmPropLong.str
FROM pmProp INNER JOIN pmPropDef ON pmProp.propid=pmPropDef.id AND pmPropDef.name = 'Area'
LEFT JOIN pmPropShort ON sid=pmProp.value
LEFT JOIN pmPropLong ON lid=-pmProp.value
WHERE pmProp.ownertype='variant' AND pmPropDef.id = pmProp.propid;
And this results in the following:
+----+------+-------+------+------+
| id | name | units | str | str |
+----+------+-------+------+------+
| 14 | Area | mm2 | 1.1 | NULL |
+----+------+-------+------+------+
The problem that I am getting both pmPropShort.str and pmPropLong.str and I should be betting one or the the other. What I really want is a single str value? How do I re-write this query to meet my needs?
You can use COALESCE which returns the first non-NULL argument. eg.
COALESCE(pmPropShort.str, pmPropLong.str)
Do you have an obvious priority?
example if long is priority
SELECT pmPropDef.id, pmPropDef.name, pmPropDef.units,
IF(pmPropLong.str IS NOT NULL,pmPropLong.str,pmPropShort.str) as str
FROM ...rest of your query
I have a table (pretty big one) with lots of columns, two of them being "post" and "user".
For a given "post", I want to know which "user" posted the most.
I was first thinking about getting all the entries WHERE (post='wanted_post') and then throw a PHP hack to find which "user" value I get the most, but given the large size of my table, and my poor knowledge of MySQL subtle calls, I am looking for a pure-MySQL way to get this value (the "user" id that posted the most on a given "post", basically).
Is it possible ? Or should I fall back on the hybrid SQL-PHP solution ?
Thanks,
Cystack
It sounds like this is what you want... am I missing something?
SELECT user
FROM myTable
WHERE post='wanted_post'
GROUP BY user
ORDER BY COUNT(*) DESC
LIMIT 1;
EDIT: Explanation of what this query does:
Hopefully the first three lines make sense to anyone familiar with SQL. It's the last three lines that do the fun stuff.
GROUP BY user -- This collapses rows with identical values in the user column. If this was the last line in the query, we might expect output something like this:
+-------+
| user |
+-------+
| bob |
| alice |
| joe |
ORDER BY COUNT(*) DESC -- COUNT(*) is an aggregate function, that works along with the previous GROUP BY clause. It tallies all of the rows that are "collapsed" by the GROUP BY for each user. It might be easier to understand what it's doing with a slightly modified statement, and it's potential output:
SELECT user,COUNT(*)
FROM myTable
WHERE post='wanted_post'
GROUP BY user;
+-------+-------+
| user | count |
+-------+-------+
| bob | 3 |
| alice | 1 |
| joe | 8 |
This is showing the number of posts per user.
However, it's not strictly necessary to actually output the value of an aggregate function in this case--we can just use it for the ordering, and never actually output the data. (Of course if you want to know how many posts your top-poster posted, maybe you do want to include it in your output, as well.)
The DESC keyword tells the database to sort in descending order, rather than the default of ascending order.
Naturally, the sorted output would look something like this (assuming we leave the COUNT(*) in the SELECT list):
+-------+-------+
| user | count |
+-------+-------+
| joe | 8 |
| bob | 3 |
| alice | 1 |
LIMIT 1 -- This is probably the easiest to understand, as it just limits how many rows are returned. Since we're sorting the list from most-posts to fewest-posts, and we only want the top poster, we just need the first result. If you wanted the top 3 posters, you might instead use LIMIT 3.