I have a query:
SELECT I.Id
, CAST(SUBSTRING_INDEX(GROUP_CONCAT(I.StatusId ORDER BY I.TransactionId DESC), ',', 1) AS UNSIGNED) AS StatusId
, SUM(I.RefundAmount) AS RefundAmount
FROM (
SELECT I.Id
, IT.Id AS TransactionId
, IT.StatusId
, IF(IT.TypeId = 2, IT.RefundAmount, 0) AS RefundAmount
FROM Items I
INNER JOIN ItemTransactions IT ON IT.ItemId = I.Id
WHERE I.Id = someValue
) I
GROUP BY I.Id
HAVING StatusId = 1 AND RefundAmount = 0
Where Items table has transaction records stored in ItemTransactions table. I've been using this type of query and works for me until this time, got some issues with the having clause.
The query works in SQL Editors but not working properly when used on stored procedures. (Don't get me wrong, I've been using this query for most of my stored procedures). Debugging line per line, found that there is an issue with having clause.
As a temporary fix, I changed the query to:
SELECT I.Id
, I.StatusId
, I.RefundAmount
FROM (
SELECT I.Id
, CAST(SUBSTRING_INDEX(GROUP_CONCAT(I.StatusId ORDER BY I.TransactionId DESC), ',', 1) AS UNSIGNED) AS StatusId
, SUM(I.RefundAmount) AS RefundAmount
FROM (
SELECT I.Id
, IT.Id AS TransactionId
, IT.StatusId
, IF(IT.TypeId = 2, IT.RefundAmount, 0) AS RefundAmount
FROM Items I
INNER JOIN ItemTransactions IT ON IT.ItemId = I.Id
WHERE I.Id = someValue
) I
GROUP BY I.Id
--HAVING StatusId = 1 AND RefundAmount = 0
) I
WHERE I.StatusId = 1 AND I.RefundAmount = 0
The query works fine. But I'd like to know if somebody has already encountered this, and found a fix. I'm using MySQL 5.0.
Thanks
WHERE clause is used to filter data on the common attributes and expressions. HAVING clause is used to filter data after groupping had been performed and it's arguments should be either the ones of the GROUP BY clause, or expressions containing aggregate functions.
It is also illegal to use the alias of the column in WHERE, GROUP BY or HAVING clauses, while it does works for the ORDER BY clause.
One option, as you've found, is to use a sub-query and then column references will work.
Another one is to duplicate the whole expression in the HAVING clause:
SELECT I.Id,
CAST(SUBSTRING_INDEX(GROUP_CONCAT(I.StatusId ORDER BY I.TransactionId DESC),
',', 1) AS UNSIGNED) AS StatusId,
SUM(I.RefundAmount) AS RefundAmount
FROM (
SELECT I.Id
, IT.Id AS TransactionId
, IT.StatusId
, IF(IT.TypeId = 2, IT.RefundAmount, 0) AS RefundAmount
FROM Items I
INNER JOIN ItemTransactions IT ON IT.ItemId = I.Id
WHERE I.Id = someValue
) I
GROUP BY I.Id
HAVING
CAST(SUBSTRING_INDEX(GROUP_CONCAT(I.StatusId ORDER BY I.TransactionId DESC),
',', 1) AS UNSIGNED) = 1
AND SUM(I.RefundAmount) = 0;
EDIT: A closer look leaded to this question Using column alias in WHERE clause of MySQL query produces an error and to the MySQL documentation, that outlines that it is possible to use column aliases in GROUP BY, HAVING and ORDER BY clauses if such aliases are quoted with backticks, like:
...
HAVING `StatusId` = 1 AND `RefundAmount` = 0
Related
I'm trying to JOIN two tables and output them so that I don't get any duplicates from the first table. In the first table there is no duplicates on the id where i'm joining the two tables, but in the second table there can be several instances.
I have tried "GROUP BY" on the id in the first table but that of course eliminates the duplicates in the second table..
Should I try to use some sort of nested SELECT or do what would you suggest?
It seems that I'm stuck and I would appreciate all help I could get.
Here is my query:
$time_query = "SELECT time.id, time.time_in, time.time_out, time.comment, time.user_id, time_break.break_in AS break_in, time_break.break_out AS break_out, time_break.time_id AS time_id
FROM `time`
LEFT JOIN time_break
ON time.id = time_break.time_id
WHERE time.time_out != '' AND time.user_id= $uid
ORDER BY time.id ASC
I am opposed to what seems like "unnatural" manipulation of results using SQL, but so that you may see how awkward this is here is an approach to suppressing (or "hiding") values that you don't want to see.
In my view this should be done at the "presentation layer", but in MySQL you may use variables to consider previous row values:
SELECT
#row_num :=IF(#prev_value = t.id , #row_num + 1, 1) AS RowNumber
, t.id
, IF(#row_num = 1,t.id, '') AS t_id
, IF(#row_num = 1,t.time_in, '') AS time_in
, IF(#row_num = 1,t.time_out, '') AS time_out
, IF(#row_num = 1,t.comment, '') AS comment
, IF(#row_num = 1,t.user_id, '') AS user_id
, tb.break_in AS break_in
, tb.break_out AS break_out
, tb.time_id AS time_id
, #prev_value := t.id
FROM `time` AS t
LEFT JOIN time_break AS tb ON t.id = tb.time_id
CROSS JOIN (SELECT #row_num :=1, #prev_value :='') vars
WHERE t.time_out <> ''
AND t.user_id = #UID
ORDER BY
t.id ASC
, t.time_in ASC
, tb.break_in ASC
NB When using the result of such a query, you should not order by any of the columns where some values are suppressed/hidden.
I have been researching this for hours and the best code that I have come up with is this from an example i found on overstack. I have been through several derivations but the following is the only query that returns the correct data, the problem is it takes over 139s (more than 2 minutes) to return only 30 rows of data. Im stuck. (life_p is a 'likes'
SELECT
logos.id,
logos.in_gallery,
logos.active,
logos.pubpriv,
logos.logo_name,
logos.logo_image,
coalesce(cc.Count, 0) as CommentCount,
coalesce(lc.Count, 0) as LikeCount
FROM logos
left outer join(
select comments.logo_id, count( * ) as Count from comments group by comments.logo_id
) cc on cc.logo_id = logos.id
left outer join(
select life_p.logo_id, count( * ) as Count from life_p group by life_p.logo_id
) lc on lc.logo_id = logos.id
WHERE logos.active = '1'
AND logos.pubpriv = '0'
GROUP BY logos.id
ORDER BY logos.in_gallery desc
LIMIT 0, 30
I'm not sure whats wrong. If i do them singularly meaningremove the coalece and one of the joins:
SELECT
logos.id,
logos.in_gallery,
logos.active,
logos.pubpriv,
logos.logo_name,
logos.logo_image,
count( * ) as lc
FROM logos
left join life_p on life_p.logo_id = logos.id
WHERE logos.active = '1'
AND logos.pubpriv = '0'
GROUP BY logos.id
ORDER BY logos.in_gallery desc
LIMIT 0, 30
that runs in less than half a sec ( 2-300 ms )....
Here is a link to the explain: https://logopond.com/img/explain.png
MySQL has a peculiar quirk that allows a group by clause that does not list all non-aggregating columns. This is NOT a good thing and you should always specify ALL non-aggregating columns in the group by clause.
Note, when counting over joined tables it is useful to know that the COUNT() function ignores NULLs, so for a LEFT JOIN where NULLs can occur don't use COUNT(*), instead use a column from within the joined table and only rows from that table will be counted. From these points I would suggest the following query structure.
SELECT
logos.id
, logos.in_gallery
, logos.active
, logos.pubpriv
, logos.logo_name
, logos.logo_image
, COALESCE(COUNT(cc.logo_id), 0) AS CommentCount
, COALESCE(COUNT(lc.logo_id), 0) AS LikeCount
FROM logos
LEFT OUTER JOIN comments cc ON cc.logo_id = logos.id
LEFT OUTER JOIN life_p lc ON lc.logo_id = logos.id
WHERE logos.active = '1'
AND logos.pubpriv = '0'
GROUP BY
logos.id
, logos.in_gallery
, logos.active
, logos.pubpriv
, logos.logo_name
, logos.logo_image
ORDER BY logos.in_gallery DESC
LIMIT 0, 30
If you continue to have performance issues then use a execution plan and consider adding indexes to suit.
You can create some indexes on the joining fields:
ALTER TABLE table ADD INDEX idx__tableName__fieldName (field)
In your case will be something like:
ALTER TABLE cc ADD INDEX idx__cc__logo_id (logo_id);
I dont really like it because ive always read that sub queries are bad and that joins perform better under stress, but in this particular case subquery seems to be the only way to pull the correct data in under half a sec consistently. Thanks for the suggestions everyone.
SELECT
logos.id,
logos.in_gallery,
logos.active,
logos.pubpriv,
logos.logo_name,
logos.logo_image,
(Select COUNT(comments.logo_id) FROM comments
WHERE comments.logo_id = logos.id) AS coms,
(Select COUNT(life_p.logo_id) FROM life_p
WHERE life_p.logo_id = logos.id) AS floats
FROM logos
WHERE logos.active = '1' AND logos.pubpriv = '0'
ORDER BY logos.in_gallery desc
LIMIT ". $start .",". $pageSize ."
Also you can create a mapping tables to speed up your query try:
CREATE TABLE mapping_comments AS
SELECT
comments.logo_id,
count(*) AS Count
FROM
comments
GROUP BY
comments.logo_id
) cc ON cc.logo_id = logos.id
Then change your code
left outer join(
should become
inner join mapping_comments as mp on mp.logo_id =cc.id
Then each time a new comment are added to the cc table you need to update your mapping table OR you can create a stored procedure to do it automatically when your cc table changes
I've been trying to figure out how I can modify this query so that the result set does not include the numHits. I want the same results in the same order, just not have the numHits included.
SELECT
`newel_inventoryKeywordIdDictionaryId`.`inventoryId`
,COUNT(`newel_inventoryKeywordIdDictionaryId`.`inventoryId`) as numHits
FROM
`newel_inventoryKeywordIdDictionaryId`
, `newel_inventoryDictionary`
WHERE
`newel_inventoryKeywordIdDictionaryId`.`dicId` = `newel_inventoryDictionary`.`dicId`
AND (
`newel_inventoryDictionary`.`word` = 'alabaster' OR `newel_inventoryDictionary`.`word` = 'chess'
)
GROUP BY inventoryId
ORDER BY numHits DESC;
sample results:
inventoryId, numHits
6928, 2
6929, 2
6924, 2
6925, 2
13772, 2
6926, 2
18203, 1
6931, 1
13863, 1
18402, 1
Desired Results:
inventoryId
6928
6929
6924
6925
13772
6926
18203
6931
13863
18402
Move the column from SELECT clause to ORDER BY clause:
SELECT
`newel_inventoryKeywordIdDictionaryId`.`inventoryId`
FROM
`newel_inventoryKeywordIdDictionaryId`
, `newel_inventoryDictionary`
WHERE
`newel_inventoryKeywordIdDictionaryId`.`dicId` = `newel_inventoryDictionary`.`dicId`
AND (
`newel_inventoryDictionary`.`word` = 'alabaster' OR `newel_inventoryDictionary`.`word` = 'chess'
)
GROUP BY inventoryId
ORDER BY COUNT(`newel_inventoryKeywordIdDictionaryId`.`inventoryId`) DESC;
SELECT
`newel_inventoryKeywordIdDictionaryId`.`inventoryId`
FROM
`newel_inventoryKeywordIdDictionaryId`
, `newel_inventoryDictionary`
WHERE
`newel_inventoryKeywordIdDictionaryId`.`dicId` = `newel_inventoryDictionary`.`dicId`
AND (
`newel_inventoryDictionary`.`word` = 'alabaster' OR `newel_inventoryDictionary`.`word` = 'chess'
)
GROUP BY inventoryId
ORDER BY COUNT(`newel_inventoryKeywordIdDictionaryId`.`inventoryId`) DESC;
You just need to put the aggregation in the ORDER BY. However, you should also:
Use explicit join syntax. Never use commas in the from clause.
Use table aliases. They make queries easier to write and to read.
Use in instead of a bunch of or statements.
Here is an improved version of the query:
SELECT kdi.inventoryId
FROM newel_inventoryKeywordIdDictionaryId kdi JOIN
newel_inventoryDictionary id
ON kdi.dicId = id.dictId
WHERE id.word IN ('alabaster', 'chess')
GROUP BY kdi.inventoryId
ORDER BY COUNT(*) DESC;
I have a contract table like this
And I want the result to look like this
What query should I use to retrieve the result like the example? I was trying to use group query but it still doesn't do what I want.
Something like this will return the specified result. It's not clear what columns you want to "match" on, I used the first four columns, because those are identical on the rows you show "grouped" together.
The "trick" is to use a GROUP BY to get the distinct list of rows you want to return. You can the use outer joins to pick out the individual rows, to match to the rows returned from the inline view (aliased as g).
This query assumes that Contract column will be unique within each "group". That is, there won't be two rows, for example, with Contract='I' for ('ENG','SWD','ABCDf','2012-11-06'). (In the sample data, it is unique, but we haven't been given any guarantee that it is unique.)
SELECT g.departemen
, g.section
, g.name
, g.start
, i.`end contract` AS `End Contract I`
, p.`end contract` AS `End Contract P`
, q.`end contract` AS `End Contract II`
FROM ( SELECT t.departemen
, t.section
, t.name
, t.start
FROM mytable t
GROUP
BY t.departemen
, t.section
, t.name
, t.start
) g
LEFT
JOIN mytable i
ON i.departemen = g.departemen
AND i.section = g.section
AND i.name = g.name
AND i.start = g.start
AND i.contract = 'I'
LEFT
JOIN mytable p
ON p.departemen = g.departemen
AND p.section = g.section
AND p.name = g.name
AND p.start = g.start
AND p.contract = 'P'
LEFT
JOIN mytable q
ON q.departemen = g.departemen
AND q.section = g.section
AND q.name = g.name
AND q.start = g.start
AND q.contract = 'II'
Tables
SubscriptionMaster (subId,subLink)
AlertMaster (alertId,alertTitle,alertText,subId)
AlertDetails (alertId,userId,voteVal,userNote)
alertId is (F.K.) to AlertMaster
Query:
SELECT alrt.alertid,
alrt.alertTitle,
alrt.alertDetails,
alrt.alertDate,
alrt.alertURL,
sub.subTitle,
avg(dtl.avgVote)
FROM alertDetails dtl,
ALERTMASTER alrt,
SUBSCRIPTIONMASTER sub
WHERE alrt.subId = 1
AND alrt.subId = sub.subId
GROUP BY alertId
ORDER BY alertDate DESC
limit 0,10;
There are many records in alertMaster for which there wudnt b any vote in alertDetails.
When I execute above query i get -0.2500 for those records. Any ways to get 0 for such records there?
What you need is a Left Join from AlertMaster to AlertDetails. However, your original query did not include any condition by which AlertDetails are related to the other two tables nor do we know from which table most of the columns are found because they do not include a table alias. Also, you should get out of the habit of using the comma-delimited syntax and should instead use the ISO Join syntax.
Lastly, you are trying to order on AlertDate but it is not included in the Group By clause. This is a "feature" of MySQL that IMO should be abandoned. The SQL specification requires that all columns in the Select clause exist in the Group By clause or a column be contained in an aggregate function. So, either group on alertDate or wrap it and the others in an aggregate function.
Select A.alertid
, Min( A.alertTitle ) As alertTitle
, Min( A.alertDetails ) As alertDetails
, Min( A.alertDate ) As alertDate
, Min( A.alertURL ) As alertUrl
, Min( S.subTitle ) As subTitle
, Coalesce( Avg( D.avgVote ), 0 ) As avgVote
From AlertMaster As A
Join SubscriptionMaster As S
On S.subid = A.subId
Left Join AlertDetails As D
On D.alertId = A.alertId
Where A.subId=1
Group By A.alertId
Order By alertDate DESC
Limit 0,10;