I have a SQL query with groupby clause
the query looks like this:
SELECT
products.product_name AS product_name,
contracts_balance.contract_prod AS contract_prod,
SUM( contracts.opt_one_firm + contracts.opt_two_firm + contracts.opt_three_firm +contracts.opt_four_firm +contracts.opt_five_firm +contracts.opt_six_firm)
AS total_open_balance
FROM
(
(
contracts_balance
LEFT JOIN products ON
(
(
contracts_balance.product_id_fk = products.product_id
)
)
)
LEFT JOIN supplier ON
(
(
products.supplier_id_fk = supplier.supplier_id
)
)
LEFT JOIN contracts ON
(
(
contracts_balance.contract_id_fk = contracts.contract_id
)
)
)
GROUP BY
products.product_name , products.pack_size
The output looks like this:
In the group by query I want the query to group by names ignoring the text inside brackets ()
So, It should return only 8 rows.
Thank you
You can use substring_index():
SELECT SUBSTRING_INDEX(p.product_name, ' (', 1) AS product_name,
cb.contract_prod AS contract_prod,
SUM( c.opt_one_firm + c.opt_two_firm + c.opt_three_firm + c.opt_four_firm + c.opt_five_firm + contracts.opt_six_firm) AS total_open_balance
FROM contracts_balance cb LEFT JOIN
products p
ON cb.product_id_fk = p.product_id LEFT JOIN
supplier s
ON p.supplier_id_fk = s.supplier_id LEFT JOIN
contracts c
ON cb.contract_id_fk = c.contract_id
GROUP BY SUBSTRING_INDEX(p.product_name, ' (', 1), cb.contract_prod;
Notes:
Table aliases make the query easier to write and read.
The unaggregated columns in the SELECT should match the GROUP BY keys. I don't understand why cb.contract_prod is in the SELECT, but not the GROUP BY and why p.pack_size is in the GROUP BY but not the SELECT.
All those parentheses and white space just makes the query look more complicated than it is.
It is also irregular to use LEFT JOINs in a query and to aggregate by a column that is not in the first table -- because it would have NULL values on non-matching rows. I suspect you want products as the first table reference in the FROM.
Related
I know that this question asked frequently but i didn't find any answer (or that its not possible):
I'v got this query
SELECT
`items`.`id`,
`items`.`part_number`,
`item_categories`.`name` AS category,
`suppliers`.`name` AS supplier,
`items`.`supplier_id`,
`items`.`name`,
`items`.`inventory`,
`items`.`package_items`,
`items`.`order_step`,
`items`.`price`,
`items`.`discount`,
`items`.`scale`,
`items`.`by_scale`,
`items`.`has_tax`,
`items`.`category_id`,
`items`.`enable`,
orders.last_orders_amount
FROM
`items`
INNER JOIN
`suppliers` ON `items`.`supplier_id` = `suppliers`.`id`
INNER JOIN
`item_categories` ON `items`.`category_id` = `item_categories`.`id`
INNER JOIN (
SELECT
GROUP_CONCAT(
JSON_EXTRACT(
`orders`.`items`,
CONCAT('$."',
"3",
'".amount')
)
ORDER BY
`orders`.`createDate` DESC
) AS last_orders_amount
FROM
`orders`
WHERE
JSON_EXTRACT(`orders`.`items`,
'$."3"') IS NOT NULL
LIMIT 4
) orders ON orders.last_orders_amount IS NOT NULL
WHERE
1
basically i want to get all the 'items' with there last 4 occurrences in 'orders' base on item.id.
I need to replace the number 3 in that query to item.id from the outer join
(i know that there is INNER JOIN cause to get only items that have occurrences in orders)
i have a table that contains some articles with it's own ID and shared SKU key.
I've tried to make the query with a left join and using group result to take all ids returned from the query.
My data structure is like that:
id - name - sku - ...
1 - felix - cat
2 - tom - cat - ...
3 - sylvester - cat - ...
4 - red - pen - ...
5 - blue - pen - ...
I tried to use this query:
SELECT * FROM `test`
[LEFT/RIGHT/INNER] JOIN
(
SELECT GROUP_CONCAT(DISTINCT id) AS idsgroup FROM `test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON id IN (bind.idsgroup);
this query is wrong, it return only 1 id per group instead all ids selected from concat or in LEFT JOIN case, obviously all rows.
Any suggestion workaround to achieve the right result?
EDIT:
here a fiddle with the structure:
http://sqlfiddle.com/#!9/b6747a
And the query i tried into:
SELECT * FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup FROM `view_test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON entity_id IN (bind.idsgroup);
As this pic show, my result lost some ids, part of the group.
EDIT 2:
after i used FIND_IN_SET() suggested by Kickstart the result is the expected:
SELECT * FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup FROM `view_test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON FIND_IN_SET(entity_id, bind.idsgroup);
The simple fix would appear to be to use FIND_IN_SET for the join. But this is a bit of a hack and will not be that quick.
SELECT *
FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup
FROM `view_test`
WHERE (attribute_name = 'sku')
GROUP BY value_name
LIMIT 0, 3
) bind
ON FIND_IN_SET(entity_id, bind.idsgroup);
Further not sure why you have a LIMIT on the sub query, especially without an order clause.
Possibly better to use a sub query to just get the DISTINCT entity_id with an attribute_name of sku and join against that.
SELECT *
FROM `view_test`
INNER JOIN
(
SELECT DISTINCT entity_id
FROM `view_test`
WHERE (attribute_name = 'sku')
) bind
ON view_test.entity_id = bind.entity_id
Something like this?
SELECT t.*, group_concat(t2.id) FROM `test` t
LEFT JOIN test t2 ON t2.attribute_name = 'sku' and t.id != t2.id
group by t.id;
row, and the list of all ids that have same SKU
How can I count rows based on a association with a third row?
In the example below I get the same result for both counts which is not the amount of "cwt_packages " nor the "cwt_products " table.
Find below the query I wrote
SELECT *, COUNT(p1.pkg_id) pkg_count, COUNT(p2.prd_id) prd_count FROM cwt_companies
LEFT JOIN cwt_packages p1 ON p1.pkg_ins_cmp = ic_id
LEFT JOIN cwt_products p2 ON p2.prd_pkg = p1.pkg_id AND p1.pkg_ins_cmp = ic_id
It's a bit hard to write a query, without knowing the structure of the tables, but something like this shoud work:
select *, count( pkg1.pkg_id ) as pkg_count,
( select count( prd.prd_id ) as prd_count
from cwt_companies c2
left join cwt_packages pkg2 on pkg2.pkg_ins_cmp = c2.ic_id
left join cwt_products prd on prd.prd_pkg = pkg2.pkg_id
where c1.ic_id = c2.ic_id ) as prd_count
from cwt_companies c1
left join cwt_packages pkg1 on pkg1.pkg_ins_cmp = c1.ic_id
group by c1.ic_id;
I've taken over a project that is a real mess so I've left with bad code structure that is forcing me to basically program in SQL. So changing the way of calculating this is for now not an option.
I have $sqlAdd variable that i need to populate in function and then concatenate that to main query to count number of lost tickets.
Main query looks like this:
$sql = "SELECT COUNT(*) as num_tickets, SUM(t.total_amount) as total_payin, SUM(t.total_payout) as total_payout
FROM t WHERE t.tickettime BETWEEN '$dateFrom' AND '$dateTo' AND t.bsid = $bsID
$sqlAdd";
So $sqlAdd is getting from another function
$sqlAdd = getSqlAdd();
And in that function i have this:
$sqlAdd = " AND 'WON' NOT IN (
SELECT GROUP_CONCAT(tr.ticketstatus)
FROM tr INNER JOIN m ON tr.ticketid = m.ticketid
WHERE tr.ticketid = t.ticketid GROUP BY m.ticket_groupid
)
AND 'PAYEDOUT' NOT IN (
SELECT GROUP_CONCAT(tr.ticketstatus)
FROM tr INNER JOIN m ON tr.ticketid = m.ticketid
WHERE tr.ticketid = t.ticketid GROUP BY m.ticket_groupid
)
AND 'CLOSED' NOT IN (
SELECT GROUP_CONCAT(tr.ticketstatus)
FROM tr INNER JOIN m ON tr.ticketid = m.ticketid
WHERE tr.ticketid = t.ticketid GROUP BY m.ticket_groupid
)
AND 'OPEN' NOT IN (
SELECT GROUP_CONCAT(tr.ticketstatus)
FROM tr INNER JOIN m ON tr.ticketid = m.ticketid
WHERE tr.ticketid = t.ticketid GROUP BY m.ticket_groupid
)";
GROUP_CONCAT(tr.ticketstatus) is getting me these rows when i execute it
CLOSED,CLOSED,CLOSED
PAYEDOUT,PAYEDOUT
CLOSED,CLOSED
WON,LOST
LOST,WON,WON,WON,WON,WON
CLOSED,CLOSED
LOST,LOST,WON
WON,WON,WON,LOST,LOST,WON,WON
LOST
I just want to count rows that have only LOST status in it. So the result should be 1. But i keep getting 7. It it counting every LOST status in results.
You would seem to want something like this:
SELECT COUNT(*) as num_tickets, SUM(t.total_amount) as total_payin,
SUM(t.total_payout) as total_payout
FROM t
WHERE t.tickettime BETWEEN '$dateFrom' AND
'$dateTo' AND t.bsid = $bsID AND
NOT EXISTS (SELECT 1
FROM tr NATURAL JOIN
m NATURAL JOIN
tg
WHERE tr.ticketid = t.ticketid AND
tr.ticketstatus IN ('WON', 'PAYEDOUT', 'CLOSED', 'OPEN')
);
Some notes:
GROUP_CONCAT() is not appropriate for this type of comparison. In SQL, you don't convert lists to strings and then do comparisons -- at least if you want performance.
You should avoid NATURAL JOIN. A small change to any of the tables can totally change the semantics of the query. In addition, it is unclear what the JOIN keys are. I would recommend USING instead.
You might want tr.ticketstatus NOT IN ('LOST') in the subquery.
So i've wrote very ugly query that is probably slow but its working i'm getting results that i need.
$sql = " AND ticket_groupid IN (
SELECT tg FROM t as tt
NATURAL JOIN tr NATURAL JOIN m
WHERE tt.tickettime BETWEEN '$dateFrom' AND '$dateTo' AND tt.bsid = $bsid
AND 'LOST' IN (
SELECT GROUP_CONCAT(tr.ticketstatus)
FROM tr NATURAL JOIN m as mm
WHERE mm.ticket_groupid = m.ticket_groupid
)
GROUP BY mm.ticket_groupid )"
Not sure if the title explains the situation right but I will try to do my best explaining here.
I have a table with 3 fields linked to other tables and I want to get all the Rows grouped in the following way:
item_id, user_id, group_id
1 2 3
2 2 3
3 4 5
4 2 4
In my query i want in comma separated format all the items_id grouped by group_id i also have some extra conditions on the WHERE clause that's why the inner join
That i can do like with this query
"SELECT
GROUP_CONCAT( DISTINCT A.item_id ) AS ids
FROM tableA A
INNER JOIN tableB B ON(tableA.id = tableB.id)
WHERE xxxxx
GROUP BY A.group_id
"
Later i can loop the results and using the comma separated to inner loop every id within the result
But i also want to group it by user_id in order to do something like this
foreach( query_results.... ){
foreach( group_id.... ){
foreach( item_id.... ){
// Display info
}
}
}
Any ideas on this?
using subqueries, we can get both itemids, userids as two seperate columns
select T1.group_id, T1.itemids, T2.userids
FROM
(SELECT group_id,
GROUP_CONCAT( DISTINCT A.item_id ) AS itemids
FROM table1 A
group by group_id) T1
INNER JOIN
(
SELECT
group_id, GROUP_CONCAT( DISTINCT A.user_id ) AS userids
FROM table1 A
group by group_id
) T2
on T1.group_id = T2.group_id
Use 2 GROUP_CONCATS
SELECT GROUP_CONCAT( DISTINCT A.item_id ) AS ids,
GROUP_CONCAT( DISTINCT A.group_id ) AS groups
FROM tableA A
INNER JOIN tableB B ON(tableA.id = tableB.id)
WHERE xxxxx
GROUP BY A.group_id
Loop your resource then use Explode on the groups and ids value and loop them both as you want