I'm trying to create a query using SELECT CASE
that would return the accumulative results.
Here is my query, it works but doesnt return the correct result.
SELECT total, count(*) as count FROM
(
SELECT case
WHEN ( X ) < 1 THEN '1 km'
WHEN ( X ) < 3 THEN '3 km'
WHEN ( X ) < 5 THEN '5 km'
WHEN ( X ) < 10 THEN '10 km'
WHEN ( X ) < 15 THEN '15 km'
WHEN ( X ) < 25 THEN '25 km'
WHEN ( X ) < 50 THEN '50 km'
WHEN ( X ) < 100 THEN '100 km'
WHEN ( X ) > 0 THEN '-1'
else '-2'
end AS `total`
FROM `store` AS d WHERE d.pending!='1'
) AS someRandomAliasHere
GROUP BY `total`
X is a formula i'm using to calculate radius from a lat and lang. total
is NOT a column in my database table, just a result to calculations of X
The query above gives me this..
1 km (4)
3 km (19)
5 km (103)
25 km (540)
50 km (61)
....
4,19,103,540,62 are the total matches found.
The total count for 3
should be 19+4=23
.
5
should be 103+19+4=122
etc. And WHEN ( X ) > 0 THEN '-1'
should show the total count. of matches
I tried using BETWEEN 0 AND 1, BETWEEN 0 AND 3
etc but it still didn't give me the correct results
Any ideas?
Another approach is to calculate the results independently then union them:
SELECT 1 AS total, COUNT(*) AS cnt FROM `store` WHERE store.pending != 1 AND ( X ) < 1
UNION ALL
SELECT 3 AS total, COUNT(*) AS cnt FROM `store` WHERE store.pending != 1 AND ( X ) < 3
UNION ALL
SELECT 5 AS total, COUNT(*) AS cnt FROM `store` WHERE store.pending != 1 AND ( X ) < 5
UNION ALL
/** ... **/
SELECT 100 AS total, COUNT(*) AS cnt FROM `store` WHERE store.pending != 1 AND ( X ) < 100
In addition to the accumulation, you also want a total value at the end with -1
. This is a bit of a pain, but it can be accomplished.
The simplest way to do cumulative sums in MySQL is using variables. The basic idea is this:
SELECT total, cnt, (@cnt := @cnt + cnt) as count
FROM (SELECT (case WHEN ( X ) < 1 THEN '1'
WHEN ( X ) < 3 THEN '3'
WHEN ( X ) < 5 THEN '5'
WHEN ( X ) < 10 THEN '10'
WHEN ( X ) < 15 THEN '15'
WHEN ( X ) < 25 THEN '25'
WHEN ( X ) < 50 THEN '50'
WHEN ( X ) < 100 THEN '100'
WHEN ( X ) > 0 THEN '-1'
else '-2'
end) AS total, COUNT(*) as cnt
FROM store s
WHERE s.pending <> '1'
GROUP BY total
) t CROSS JOIN
(SELECT @cnt := 0) vars
ORDER BY total;
The issue with this is that you will not get an overall total of the non-negative values. Let me assume that you have no negative values. This requires adding another row into the total line:
SELECT total, cnt, (@cnt := @cnt + cnt) as count
FROM (SELECT (case WHEN ( X ) < 1 THEN '1'
WHEN ( X ) < 3 THEN '3'
WHEN ( X ) < 5 THEN '5'
WHEN ( X ) < 10 THEN '10'
WHEN ( X ) < 15 THEN '15'
WHEN ( X ) < 25 THEN '25'
WHEN ( X ) < 50 THEN '50'
WHEN ( X ) < 100 THEN '100'
WHEN ( X ) > 0 THEN '-1'
else '-2'
end) AS total, COUNT(*) as cnt
FROM store s
WHERE s.pending <> '1'
GROUP BY `total`
UNION ALL
SELECT -1, 0
) t CROSS JOIN
(SELECT @cnt := 0) vars
ORDER BY (total >= 0) desc, total;
I've changed the order by
as well. Note that the value -2
is probably meaningless, because X < 1
and X > 0
cover all possible values of X
(except for NULL
). If you actually have values 100 or greater, there are some small changes to refine the query. You do not describe what to do with those values, so clarification on the question would be helpful.
Not sure if this works since I don't have a database to test this on. Also not exactly in the format you want.
select sum(if(X<1,1,0)) as C1,
sum(if(X<3,1,0)) as C3,
sum(if(X<5,1,0)) as C5,
sum(if(X<10,1,0)) as C10,
sum(if(X<15,1,0)) as C15,
sum(if(X<25,1,0)) as C25,
sum(if(X<50,1,0)) as C50,
sum(if(X<100,1,0)) as C100,
sum(if(X>=100,1,0)) as C100P
from store
where store.pending != '1'
Unfortunatelly MySQL do not have analytical functions and windowed functions, but in this case, you can achieve your goal using a variable and a nested subquery:
SELECT
total,
cnt,
@rollupCount:=@rollupCount+cnt AS rollupCount
FROM
(
SELECT
total,
count(*) AS cnt
FROM
(
SELECT
CASE
WHEN ( X ) < 1 THEN '1'
WHEN ( X ) < 3 THEN '3'
WHEN ( X ) < 5 THEN '5'
WHEN ( X ) < 10 THEN '10'
WHEN ( X ) < 15 THEN '15'
WHEN ( X ) < 25 THEN '25'
WHEN ( X ) < 50 THEN '50'
WHEN ( X ) < 100 THEN '100'
WHEN ( X ) > 0 THEN '-1'
ELSE '-2'
END AS `total`
FROM
`store` AS d
WHERE
d.pending != '1'
) AS someRandomAliasHere
GROUP BY
`total`
) AS anotherRandomAliasHere
, (SELECT @rollupCount:=0) AS RC
ORDER BY
total ASC
This is the same as when you want to calculate the row number for each record:
SELECT
@rowNumber:=@rowNumber+1 AS rowNumber,
sourceColumns
FROM
sourceTable, (SELECT @rowNumber:=0) AS t
ORDER BY
orderColumn;
A column to give cumulative totals from an existing query can be created relatively simply:
SELECT X,
total,
(SELECT SUM(total)
FROM (<<<your_current_query>>>) ycq2
WHERE ycq2.X <= ycq1.X) `cumulative_total`
FROM (<<<your_current_query>>>) ycq1
Of course this will expand quite a lot when pasting your current query in the two marked places.
See SQL fiddle demo .
try this:
/sql server version/
DECLARE @GROUPS TABLE (TOTAL INT)
INSERT INTO @GROUPS
VALUES (1),
(3),
(5),
(10),
(15),
(25),
(50),
(100),
(-1)
SELECT a.TOTAL, z.[COUNT] FROM @GROUPS a CROSS APPLY (SELECT COUNT(*) as [COUNT] FROM store x WHERE CASE WHEN a.TOTAL = -1 THEN -x.X ELSE x.X END < REPLACE(a.TOTAL,-1,0) AND pending != 1) z
/mysql version/
CREATE TEMPORARY TABLE GROUPS TABLE (TOTAL INT) INSERT INTO GROUPS VALUES (1), (3), (5), (10), (15), (25), (50), (100), (-1)
SELECT a.TOTAL, z.[COUNT] FROM GROUPS a CROSS APPLY (SELECT COUNT(*) as [COUNT] FROM store x WHERE CASE WHEN a.TOTAL = -1 THEN -x.X ELSE x.X END < REPLACE(a.TOTAL,-1,0) AND pending != 1) z
Your question is a bit hard to understand. Is this what you want?
CREATE TABLE Totals (X INT, SUM INT);
INSERT INTO Totals VALUES
(1, 4),
(3, 19),
(5, 103),
(25, 540),
(50, 61)
SELECT first.X
, Sum(second.SUM)
FROM Totals first
JOIN Totals second
ON first.x >= second.x
GROUP BY first.X
UNION
SELECT 0, SUM(sum) * 2
FROM Totals
try this one :
1.) value after "THEN" must be numeric
2.) make temporary table with running id (example for SQL SERVER)
SELECT identity(int, 1, 1) as id,
case
WHEN ( X ) < 1 THEN 1
WHEN ( X ) < 3 THEN 3
WHEN ( X ) < 5 THEN 5
WHEN ( X ) < 10 THEN 10
WHEN ( X ) < 15 THEN 15
WHEN ( X ) < 25 THEN 25
WHEN ( X ) < 50 THEN 50
WHEN ( X ) < 100 THEN 100
WHEN ( X ) > 0 THEN -1
else '-2' end AS total
Into storeID
FROM store AS d
WHERE d.pending!='1'
Order BY total
3.) join table with same table, with criteria
Select a.*, sum(b.total) as NewTotal
From storeID a
Left Join storeID b
On b.id <= a.id
Group By a.id, a.total
4.) i think, "NewTotal" is what you are looking for
I suppose that you X function returns a floating point number. If I understand your logic correctly, you want to group together values where X is >=0 and <1
, where X >=0 and <3
, >=0 and <5
and so on, and you want to return -1 when the value is >=0
, and -2 when the value is a negative <0
number.
I would use an intervals
table, defined like this:
CREATE TABLE intervals (
i_begin INT,
i_end INT,
i_value INT
);
INSERT INTO intervals VALUES
(0, 1, 1),
(0, 3, 3),
(0, 5, 5),
(0, 10, 10),
(0, 15, 15),
(0, 25, 25),
(0, 50, 50),
(0, 100, 100),
(0, null, -1),
(null, 0, -2);
or you can play with values in this table to make it suite your needs.
then you can just use an INNER JOIN and a GROUP BY query:
SELECT
i_value, COUNT(*)
FROM
store INNER JOIN intervals
ON ((i_begin IS NULL OR X>=i_begin) AND (i_end IS NULL OR X<i_end))
WHERE
store.pending<>1
GROUP BY
i_value
Please see an example here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.