SQL printing all in one row - mysql

I want to print data in two columns, like:
Count Category
But my code prints all in one row, like:
Count Category Count Category
Any tips?
Select sum(Case when population >= 1000000 and population < 5000000 then 1 else 0 end) as Count, '1 000 000 - 4 999 999' as Category,
sum(Case when population >= 500000 and population < 100000 then 1 else 1 end) as Count, '100 000 - 499 999' as Category,
sum(Case when population >= 500000 and population < 100000 then 1 else 1 end) as Count, '500 000 - 999 999' as Category,
sum(Case when population >= 500000 and population < 100000 then 1 else 1 end) as Count, 'Under 100 000' as Category,
sum(Case when population >= 500000 and population < 100000 then 1 else 1 end) as Count, 'Over 5 million' as Category
from cities

Select sum(Case when population >= 1000000 and population < 5000000 then 1 else 0 end) as Count, '1 000 000 - 4 999 999' as Category FROM cities
UNION
SELECT sum(Case when population >= 500000 and population < 100000 then 1 else 1 end) as Count, '100 000 - 499 999' as Category FROM cities
and so on...
Concat all single resuts in one result table with UNION keyword
your else in case part should be everywhere 0 not 1 if you just want to count the cities...
A Better solution anyway would be
SELECT COUNT(cities.primKey) as Count, '1 000 000 - 4 999 999' as Category FROM cities WHERE population >= 1000000 and population < 5000000
UNION
SELECT COUNT(cities.primKey) as Count, '1 000 000 - 4 999 999' as Category FROM cities WHERE population >= 500000 and population < 100000
UNION ...

SELECT CASE WHEN Population >= 5000000
THEN 'Over 5 million'
WHEN Population >= 1000000 AND Population < 5000000
THEN '1 000 000 - 4 999 999'
WHEN Population >= 500000 AND Population < 1000000
THEN '500 000 - 999 999'
WHEN Population >= 100000 AND Population < 500000
THEN '100 000 - 499 999'
WHEN Population < 100000
THEN 'Under 100 000'
END [Category]
,COUNT(*) [Count]
FROM Cities

If you want to get all categories even if there are no cities that belong to it, you can do:
select sum(if(ci.population is null, 0, 1)) as 'Count', cat.Category
from (
select 0 as 'low', 100000 as 'high', 'Under 100 000' as Category, 1 as 'ord'
union
select 100000, 499999, '100 000 - 499 999', 2
union
select 500000, 999999, '500 000 - 999 999', 3
union
select 1000000, 4999999, '1 000 000 - 4 999 999', 4
union
select 5000000, null, 'Over 5 million' , 5
) as cat
left join cities ci on ci.population between cat.low and ifnull(cat.high, ci.population)
group by cat.Category, cat.ord
order by cat.ord

Related

SQL display age range

I'm writing a query for age range, in which I want to show the count of people of all age ranges eg
AGE PEOPLE
"0-10" 0
"11-20" 2
"21-30" 5
"31-40" 0
"41-50" 1
I've tried using
SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS [Under 10],
SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS [11-20],
SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) AS [21-30]
FROM people
But it shows ranges as column names
0-10 11-20 21-30 31-40 41-50
0 2 5 0 1
which i dont want.
I have also tried GROUP BY but it didn't show the ranges in which the count was 0.
You can use UNION ALL:
SELECT '[Under 10]' as Age, SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) as People
FROM people
UNION ALL
SELECT '[11-20]', SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END)
FROM people
UNION ALL
SELECT '[21-30]', SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END)
FROM people;
you case when should be like below
CASE WHEN age < 10 then '0-10'
when age age BETWEEN 11 AND 20 then '11-20'
when age BETWEEN 21 AND 30 then '21-30'
..... end as agegroup,--put here more according to your need
count(*)
from table group by agegroup
You need to perform UNION All for this.
SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS PEOPLE, 'UNDER 10' AS AGE FROM people
UNION ALL
SELECT SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS PEOPLE, `11-20` FROM people
UNION ALL
SELECT SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) , `21-30` FROM people
You want to get the group of result in rows so need to perform UNION in this case.
Please find this link for more info on UNION in MYSQL.link
If you are going to use UNION, use UNION ALL and move the conditions to the WHERE clause:
SELECT '[Under 10]' as Age, COUNT(*)
FROM people
WHERE age < 10
UNION
SELECT '[11-20]', COUNT(*)
FROM people
WHERE BETWEEN 11 AND 20
UNION ALL
SELECT '[21-30]', COUNT(*)
FROM people
WHERE age BETWEEN 21 AND 30;
Filtering and UNION ALL both improve performance. (UNION incurs overhead for removing duplicates).
There are other approaches. For instance, you can unpivot your table:
SELECT grp.age,
(CASE grp
WHEN 1 THEN [Under 10]
WHEN 2 THEN [11-20]
WHEN 3 THEN [21-30]
END)
FROM (SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS [Under 10]
SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS [11-20],
SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) AS [21-30]
FROM people p
) p CROSS JOIN
(SELECT 1 as grp, '[Under 10]' as age UNION ALL
SELECT 2 as grp, '[11-20]' as age UNION ALL
SELECT 3, as grp, '[21-30]' as age
) grps;
Although this looks more complicated, it is much better from a performance perspective, because it only scans the original table once.
There are other variants as well that only touch the original table once.

MYSQL Select priority data if exist

My current data :
Month Price A Price B Status Approval
January 1000 2000 1 0
February 1000 2000 1 0
March 1000 2000 1 0
April 1000 2000 1 0
May 1000 2000 1 0
June 1000 2000 1 0
July 1000 2000 1 0
August 1000 2000 1 0
September 1000 2000 1 0
October 1000 3000 1 0
October 2000 2000 2 0
October 3000 2000 2 1
November 1000 2000 1 0
December 1000 2000 1 0
*Status 1 = Not Changed, Status 2 = Changed, Approval 1 = Approved
What i'm trying to show,is when month > 1 (my case is October)
,IF STATUS = 2 AND APPROVAL = 1 IS NOT EXIST,Show data with
STATUS = 1 AND APPROVAL = 0ELSE SHOW STATUS = 2 AND APPROVAL =
1 Using group by, doesn't work,it shows first data if month more
than 1
My Query :
SELECT A.NAME AS MONTH,
IFNULL( B.PRICE_A, 0 ) PRICE_A,
IFNULL( B.PRICE_B, 0 ) PRICE_B,
STATUS,
APPROVAL,
FROM
REF_MONTH A
LEFT OUTER JOIN (
SELECT
SUBSTR( PERIOD, '5,2' ) MONTH,
ROUND( PRICE_A_FIX, 2 ) PRICE_A,
ROUND( PRICE_B_FIX, 2 ) PRICE_B,
A.STATUS,
A.APPROVAL,
FROM
PRICE_MONTH_LIST A
WHERE
SUBSTR( PERIOD, 1, 4 ) = 2018
) B ON B.MONRH= A.MONTH
WHERE DATE_FORMAT( STR_TO_DATE( CONCAT( 2018, month), '%Y%m' ), '%Y%m' ) <=
DATE_FORMAT( SYSDATE( ), '%Y%m' )
GROUP BY A.MONTH
ORDER BY A.ID;
My Expected Result :
Month Price A Price B Status Approval
January 1000 2000 1 0
February 1000 2000 1 0
March 1000 2000 1 0
April 1000 2000 1 0
May 1000 2000 1 0
June 1000 2000 1 0
July 1000 2000 1 0
August 1000 2000 1 0
September 1000 2000 1 0
October 3000 2000 2 1
November 1000 2000 1 0
December 1000 2000 1 0
Below would be the query -
with table1 as
(
select 'January' as month, 1000 as priceA, 2000 as priceB, 1 as status, 0 as approval from dual
union all
select 'October' as month, 1000 as priceA, 3000 as priceB, 1 as status, 0 as approval from dual
union all
select 'October' as month, 2000 as priceA, 2000 as priceB, 2 as status, 0 as approval from dual
union all
select 'October' as month, 3000 as priceA, 2000 as priceB, 2 as status, 1 as approval from dual
)
,t1 as
(
select month, count(*) as cnt, min(status) as min_status,
max(status) max_status, min(approval) min_app, max(approval) max_app from table1
group by month
)
,t2 as
(
select
month,
case when cnt>1 and max_status<>2 and max_app<>1 then min_status else max_status end as status1,
case when cnt>1 and max_status<>2 and max_app<>1 then min_app else max_app end as approval1
from t1
)
select
t.month,
t.priceA,
t.priceB,
t.status,
t.approval
from table1 t
inner join t2 on t.month=t2.month and t.status=t2.status1 and t.approval=t2.approval1;
result -
January 1000 2000 1 0
October 3000 2000 2 1
As there were no insert scripts, I have use CTE with oralce db.
Hope this helps.

MySQL: Using ORDER BY and UNION

Please take a look at the following query:
INSERT INTO product_filter (product_id,filter_id)
SELECT product_id,
(CASE WHEN price < 100 then 1
WHEN price >= 100 AND price < 500 then 2
WHEN price >= 500 AND price < 1000 then 3
WHEN price >= 1000 AND price < 1500 then 4
WHEN price >= 1500 AND price < 2000 then 5
WHEN price >= 2000 AND price < 2500 then 50
WHEN price >= 2500 AND price < 3000 then 6
ELSE 51 END) AS filter_id
FROM product_special
ORDER BY priority DESC, date_end DESC
LIMIT 1
UNION
SELECT product_id,
(CASE WHEN price < 100 then 1
WHEN price >= 100 AND price < 500 then 2
WHEN price >= 500 AND price < 1000 then 3
WHEN price >= 1000 AND price < 1500 then 4
WHEN price >= 1500 AND price < 2000 then 5
WHEN price >= 2000 AND price < 2500 then 50
WHEN price >= 2500 AND price < 3000 then 6
ELSE 51 END) AS filter_id
FROM product WHERE product_id not IN
(SELECT product_id FROM product_special)
This is what the query is supposed to do:
select all special prices from the product_special table and depending on the price, associate them to different price filters
every product can have multiple special prices, so just pick the one that has higher priority and will last longer
select all regular prices from the product table (only those that don't have special price in the product_special table) and depending on the price, associate them to different price filters
Error I'm receiving:
Error Code: 1221. Incorrect usage of UNION and ORDER BY
Sample Data sets:
Products
Specials
Any help is appreciated.
When you use UNION queries with LIMIT or ORDER BY for each separate query then you need to organize your queries using brackets like
(query 1 with limit order by )
UNION
(query 1 with limit order by )
Your above query can be written as to avoid this error
INSERT INTO product_filter (product_id,filter_id)
(
SELECT product_id,
(CASE WHEN price < 100 then 1
WHEN price >= 100 AND price < 500 then 2
WHEN price >= 500 AND price < 1000 then 3
WHEN price >= 1000 AND price < 1500 then 4
WHEN price >= 1500 AND price < 2000 then 5
WHEN price >= 2000 AND price < 2500 then 50
WHEN price >= 2500 AND price < 3000 then 6
ELSE 51 END) AS filter_id
FROM product_special
ORDER BY priority DESC, date_end DESC
LIMIT 1 )
UNION
(SELECT product_id,
(CASE WHEN price < 100 then 1
WHEN price >= 100 AND price < 500 then 2
WHEN price >= 500 AND price < 1000 then 3
WHEN price >= 1000 AND price < 1500 then 4
WHEN price >= 1500 AND price < 2000 then 5
WHEN price >= 2000 AND price < 2500 then 50
WHEN price >= 2500 AND price < 3000 then 6
ELSE 51 END) AS filter_id
FROM product WHERE product_id not IN
(SELECT product_id FROM product_special)
)
You need to use a sub-query for the ORDER BY and LIMIT in the first query.
Something like this:
INSERT INTO product_filter (product_id,filter_id)
SELECT * FROM
(SELECT product_id,
(CASE WHEN price < 100 then 1
WHEN price >= 100 AND price < 500 then 2
WHEN price >= 500 AND price < 1000 then 3
WHEN price >= 1000 AND price < 1500 then 4
WHEN price >= 1500 AND price < 2000 then 5
WHEN price >= 2000 AND price < 2500 then 50
WHEN price >= 2500 AND price < 3000 then 6
ELSE 51 END) AS filter_id
FROM product_special
ORDER BY priority DESC, date_end DESC
LIMIT 1) a
UNION
SELECT product_id,
(CASE WHEN price < 100 then 1
WHEN price >= 100 AND price < 500 then 2
WHEN price >= 500 AND price < 1000 then 3
WHEN price >= 1000 AND price < 1500 then 4
WHEN price >= 1500 AND price < 2000 then 5
WHEN price >= 2000 AND price < 2500 then 50
WHEN price >= 2500 AND price < 3000 then 6
ELSE 51 END) AS filter_id
FROM product WHERE product_id not IN
(SELECT product_id FROM product_special)
You should round brackets in order to use ORDER/LIMIT on individual queries
For example
(SELECT * FROM table1 WHERE ... ORDER BY field1 LIMIT 0, 1)
UNION
(SELECT * FROM table1 WHERE ...)
If you use Order BY outside brackets , it applies to UNIONED result

MySQL count listing between certain price range

From another post on the site, I took the following type of query. They were using sum, I am looking for a count of listings.
SELECT
COUNT(IF(sold_price < 20,1,0)) as 'Under 20',
COUNT(IF(sold_price BETWEEN 20 and 50,1,0)) as '20 - 50',
COUNT(IF(sold_price BETWEEN 50 and 100,1,0)) as '50 - 100',
COUNT(IF(sold_price BETWEEN 100 and 250,1,0)) as '100 - 250',
COUNT(IF(sold_price BETWEEN 250 and 500,1,0)) as '250 - 500',
COUNT(IF(sold_price BETWEEN 500 and 1000,1,0)) as '500 - 1000',
COUNT(IF(sold_price BETWEEN 1000 and 2000,1,0)) as '1000 - 2000',
COUNT(IF(sold_price > 2000,1,0)) as 'Over 2000'
FROM listings
where current_batch = 'Y'
All my results are coming back as the same number
Under 20 20 - 50 50 - 100 100 - 250 250 - 500 500 - 1000 1000 - 2000 Over 2000
94665 94665 94665 94665 94665 94665 94665 94665
Does anyone have any suggestion on show to do this or if this can be done with count?
MySQL documentation says the following(http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html#function_count):
COUNT(expr)
Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement
So replace:
IF(sold_price BETWEEN 20 and 50,1,0)
by
IF(sold_price BETWEEN 20 and 50,1,NULL)
Use SUM instead of COUNT in your query like:
SELECT
SUM(CASE WHEN our_price <= 10000 THEN 1 ELSE 0 END) AS p10000,
SUM(CASE WHEN our_price >= 10001 AND our_price <= 25000 THEN 1 ELSE 0 END) AS p25000,
SUM(CASE WHEN our_price >= 25001 AND our_price <= 50000 THEN 1 ELSE 0 END) AS p50000,
SUM(CASE WHEN our_price >= 50001 AND our_price <= 75000 THEN 1 ELSE 0 END) AS p75000,
SUM(CASE WHEN our_price >= 75001 AND our_price <= 100000 THEN 1 ELSE 0 END) AS p100000,
SUM(CASE WHEN our_price >= 100001 AND our_price <= 150000 THEN 1 ELSE 0 END) AS p150000,
SUM(CASE WHEN our_price >= 151000 AND our_price <= 200000 THEN 1 ELSE 0 END) AS p200000,
SUM(CASE WHEN our_price > 200000 THEN 1 ELSE 0 END) AS p200001
FROM products;

Grouping items between 2 numbers

I have a query that looks like this:
select
price,
item_id,
sum(price),
count(item_id)
from transactions
group by
(price <= 20),
(price between 21 and 30),
(price between 31 and 40),
(price between 41 and 50),
(price > 50)
I have never done a group like this before when I wrote it I was just guessing to see if the query was even valid, and it was. But my question is, is it really getting me what I want?
I want all transactions grouped by:
Items that cost less than or equal to $20
Items that cost between $21 and $30
Items that cost between $31 and $40
Items that cost between $41 and $50
Items that cost more than $50
So, is that query doing what I am asking?
The way to do this in standard SQL (and MySQL) is to use the case statement. Also, I put the definition in a subquery like this:
select pricegrp, sum(price), count(item_id)
from (select t.*,
(case when price <= 20 then '00-20'
when price between 21 and 30 then '21-30'
when price between 31 and 40 then '31-40'
when price between 41 and 50 then '41-50'
when price > 50 then '50+'
end) as pricegrp
from transactions t
) t
group by pricegrp
Also, do you want to group by item_id as well? Or are you just trying to return one arbitrary item? Based on what you want, I'm removing the item_id from the select clause. It doesn't seem necessary.
Your query actually does work in MySQL, in the sense that it runs. It is going to produce one row for each group that you want, so in that sense it "works". However, within each group, it is going to choose an arbitrary price and item_id. These are not explicitly mentioned in the group by clause, so you are using a MySQL (mis)feature called Hidden Columns. Different runs of the query or slight changes to the data or slight changes to the query can change the values of price and item_id returned for each group.
I strongly suggest that you actually name the group. This makes the query and the output much clearer.
Also, I recommend that you get in the habit of putting all columns in the select in the group by clause. There are a few cases where hidden columns are actually useful, but I think, in general, you should depend on them sparingly.
If the price is not stored as an integer, then correct logic is:
select pricegrp, sum(price), count(item_id)
from (select t.*,
(case when price <= 20 then '00-20'
when price <= 30 then '21-30'
when price <= 40 then '31-40'
when price <= 50 then '41-50'
when price > 50 then '50+'
end) as pricegrp
from transactions t
) t
group by pricegrp
SELECT
price,
item_id,
sum(price),
count(item_id),
IF(price<=20,0,IF(price<=30,1,IF(price<=40,2,IF(price<=50,3,4)))) AS pricegroup
FROM transactions
GROUP BY pricegroup
or even
SELECT
price,
item_id,
sum(price),
count(item_id)
FROM transactions
GROUP BY
IF(price<=20,0,IF(price<=30,1,IF(price<=40,2,IF(price<=50,3,4))))
SELECT price,
item_id,
SUM(CASE WHEN price <= 20 THEN price ELSE 0 END) `(price <= 20) SUM`,
SUM(CASE WHEN price <= 20 THEN 1 ELSE 0 END) `(price <= 20) COUNT`,
SUM(CASE WHEN price between 21 and 30 THEN price ELSE 0 END) `(price <= 20) SUM`,
SUM(CASE WHEN price between 21 and 30 THEN 1 ELSE 0 END) `(price <= 20) COUNT`,
SUM(CASE WHEN price between 31 and 40 THEN price ELSE 0 END) `price between 31 and 40 SUM`,
SUM(CASE WHEN price between 31 and 40 THEN 1 ELSE 0 END) `price between 31 and 40 COUNT`,
SUM(CASE WHEN price between 41 and 50 THEN price ELSE 0 END) `price between 41 and 50 SUM`,
SUM(CASE WHEN price between 41 and 50 THEN 1 ELSE 0 END) `price between 41 and 50 COUNT`,
SUM(CASE WHEN price > 50 THEN price ELSE 0 END) `price > 50 SUM`,
SUM(CASE WHEN price > 50 THEN 1 ELSE 0 END) `price > 50 COUNT`
FROM transactions
GROUP BY price, item_id