How to convert this SQL-Statement to PostgreSQL? - mysql

I have a MySQL Statement which won't run on a PostgreSQL Server.
Here is the original MySQL Query:
SELECT count(*) AS howmany
FROM members
WHERE member_status =1
AND member_sex = 'male'
AND (YEAR( '2015-12-31' ) - YEAR( member_birthdate ) ) -
( RIGHT( '2015-12-31', 5 ) < RIGHT( member_birthdate, 5 ) )
BETWEEN 27 AND 40;
This is my approach:
SELECT COUNT(*) AS howmany FROM members
WHERE member_status =1
AND member_sex ='male'
AND (EXTRACT(YEAR FROM '2015-12-31'::date) - EXTRACT(YEAR FROM member_birthdate))
BETWEEN 27 AND 40;
The Goal is, that i want to know how many Members are between 27 and 40 Years old on the qualifying date 2015-12-31.
I don't know how to convert the RIGHT Part of the Query.

You can use AGE function:
SELECT COUNT(*) AS howmany
FROM members
WHERE member_status =1
AND member_sex ='male'
AND extract(year from age(timestamp '2015-12-31', member_birthdate))
BETWEEN 27 AND 40;

Related

SQL numbering merge if string is the same

I want to create a stats page for my website. However I have the problem that if I run my query it will not be merged if the sting is the same. Any idea how to fix this?
My query:
SELECT * FROM (
SELECT dj_1, count(*) AS uren
FROM dj_rooster
WHERE week = '28' GROUP BY id_1
ORDER BY count(*) DESC
) x
UNION ALL
SELECT * FROM (
SELECT dj_1, count(*) AS uren
FROM djpaneel_shows_uren
WHERE week = '28' AND active = '1'
GROUP BY dj_1
ORDER BY count(*) DESC
) x
My results:
Jack - 7
Jeremy - 5
Jack - 1
Thanks in advance for your help
There is no need to aggregate twice or more.
Use UNION ALL to get all the rows from the 2 tables and then aggregate:
SELECT t.dj_1, COUNT(*) AS uren
FROM (
SELECT dj_1 FROM dj_rooster
WHERE week = '28'
UNION ALL
SELECT dj_1 FROM djpaneel_shows_uren
WHERE week = '28' AND active = '1'
) t
GROUP BY t.dj_1
ORDER BY uren DESC

How to group by date

Below I cannot do Group by Date the following figures.
I have tried to put Group By in different lines, but not working.
SELECT SUM(a.NetAmount) AS TotalDonation
FROM (
SELECT
(
CASE WHEN bt.BalanceTransactionCurrencyID = 17
THEN bt.BalanceTransactionNet
ELSE
bt.BalanceTransactionNet * (SELECT TOP 1 ExrateValue FROM Exrate WHERE ExrateDate < bt.BalanceTransactionCreated AND bt.BalanceTransactionCurrencyID = CurrencyID ORDER BY ExrateDate Desc)
END
) AS NetAmount
FROM Charge as ch
JOIN BalanceTransaction as bt ON (ch.BalanceTransactionID = bt.BalanceTransactionID)
WHERE ch.ChargeCreatedDate BETWEEN '3-1-2019' AND '3-31-2019'
) AS a
I wanted to see:
Days Total Amount
March 1 xxxx
March 2 xxxx
March 3 xxx
MySQL does not use TOP. Use LIMIT:
SELECT ChargeCreatedDate, SUM(netamount)
FROM (SELECT ch.ChargeCreatedDate,
(CASE WHEN bt.BalanceTransactionCurrencyID = 17
THEN bt.BalanceTransactionNet
ELSE bt.BalanceTransactionNet * (SELECT e.ExrateValue
FROM Exrate e
WHERE e.ExrateDate < bt.BalanceTransactionCreated AND
e.CurrencyID = bt.BalanceTransactionCurrencyID
ORDER BY ExrateDate Desc
LIMIT 1
)
END) AS NetAmount
FROM Charge ch JOIN
BalanceTransaction bt
ON ch.BalanceTransactionID = bt.BalanceTransactionID
WHERE ch.ChargeCreatedDate BETWEEN '2019-03-01' AND '2019-03-31'
) chtbt
GROUP BY ChargeCreatedDate;
If you happen to be using SQL Server, you can replace the LIMIT 1 with FETCH FIRST 1 ROW ONLY.
The following code will hopefully display what you are looking for
SELECT a.Days AS Days, SUM(cast(a.NetAmount as decimal(16,9))) AS TotalDonation
FROM (
SELECT
(
CASE WHEN bt.BalanceTransactionCurrencyID = 17
THEN bt.BalanceTransactionNet
ELSE
bt.BalanceTransactionNet * (SELECT TOP 1 ExrateValue FROM Exrate WHERE ExrateDate < bt.BalanceTransactionCreated AND bt.BalanceTransactionCurrencyID = CurrencyID ORDER BY ExrateDate Desc)
END
) AS NetAmount,
ch.ChargeCreatedDate as Days
FROM Charge as ch
JOIN BalanceTransaction as bt ON (ch.BalanceTransactionID = bt.BalanceTransactionID)
WHERE ch.ChargeCreatedDate BETWEEN '3-1-2019' AND '3-31-2019'
) AS a GROUP BY a.Days
This should be sufficient. You need to SELECT the desired value in the query in order to get it to show up. Also when using the SUM() function you need to specify what the group value will be.

How to show all Case When Then records even if NULL in MySQL

i have to display how many users voted for poll 2, but separated in age ranges.
My current query works fine; but I'm having trouble when a specific range doesn't have any users in it. For example, if no users between the ages of 40-49 voted for poll 2, then the result should be:
age_1 [13-19] = 0
age_2 [20-29] = 3
age_3 [30-39] = 1
age_4 [40-49] = 0
age_5 [50+] = 0
...but instead I'm getting this:
age_2 [20-29] = 3
age_3 [30-39] = 1
So, what I need is for all age ranges to appear in the results, even when there are no matches. This is my current query:
SELECT
CASE
WHEN age BETWEEN 13 AND 19 THEN "age_1"
WHEN age BETWEEN 20 and 29 THEN "age_2"
WHEN age BETWEEN 30 and 39 THEN "age_3"
WHEN age BETWEEN 40 and 49 THEN "age_4"
WHEN age >= 50 THEN "age_5"
END as age_range,
COUNT(*) AS q
FROM (SELECT user.id_user AS id_user, TIMESTAMPDIFF(YEAR, user.birthday, CURDATE()) AS age FROM user) AS udata
JOIN poll_vote ON poll_vote.id_user = udata.id_user
WHERE poll_vote.id_poll = 2 AND udata.age IS NOT NULL
GROUP BY age_range
ORDER BY age_range
Here's the SQL Fiddle with the example data. Thanks!
I'd use a left join with your totals and coalesce.
select totals.age_range, coalesce(ages.q,0) total from
(
select 'age_1' age_range
union select 'age_2' age_range
union select 'age_3' age_range
union select 'age_4' age_range
union select 'age_5' age_range ) totals
left join (
SELECT
CASE
WHEN age BETWEEN 13 AND 19 THEN "age_1"
WHEN age BETWEEN 20 and 29 THEN "age_2"
WHEN age BETWEEN 30 and 39 THEN "age_3"
WHEN age BETWEEN 40 and 49 THEN "age_4"
WHEN age >= 50 THEN "age_5"
END as age_range,
COUNT(*) AS q
FROM (SELECT user.id_user AS id_user, TIMESTAMPDIFF(YEAR, user.birthday, CURDATE()) AS age FROM user) AS udata
JOIN poll_vote ON poll_vote.id_user = udata.id_user
WHERE poll_vote.id_poll = 2 AND udata.age IS NOT NULL
GROUP BY age_range
ORDER BY age_range
) ages
on totals.age_range = ages.age_range
-- results
age_range total
age_1 0
age_2 3
age_3 1
age_4 0
age_5 0

SQL — segment by groups [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions must demonstrate a minimal understanding of the problem being solved. Tell us what you've tried to do, why it didn't work, and how it should work. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
In a MySQL DB, I have a purchases table that has these columns:
USERID PURCHASE_AMOUNT
3 20
9 30
3 5
4 5
1 10
1 5
I would like to generate a report like this
SUM_OF_PURCHASES_RANGE NUM_OF_USERS
0-1 0
1-5 1
5-20 1
20-30 2
Where it means: there are 0 users who bought up to 1(SUM of purchases) (inclusive), there are 1 users who bought between 1 to 5 etc...
What query should I use to generate it?
You can create the range using a UNION, and just LEFT JOIN to that to get all categories; (edited for your change in the desired result)
SELECT CONCAT(base.lower,'-',base.upper) PURCHASE_RANGE, COUNT(userid) NUM_OF_USERS
FROM (
SELECT 0 lower, 1 upper UNION SELECT 2, 5 UNION SELECT 6,20 UNION SELECT 21,30
) base
LEFT JOIN (
SELECT userid, SUM(purchase_amount) pa FROM purchases GROUP BY userid
) p
ON p.pa >= base.lower AND p.pa <= base.upper
GROUP BY base.upper
An SQLfiddle to test with.
More easier syntax :
SELECT PURCHASE_RANGE , COUNT(*) as NUM_OF_USERS
FROM
(
SELECT
CASE
WHEN PURCHASE_AMOUNT <= 1 THEN 1
WHEN PURCHASE_AMOUNT > 1 AND PURCHASE_AMOUNT <= 5 THEN 5
WHEN PURCHASE_AMOUNT > 5 AND PURCHASE_AMOUNT <= 10 THEN 10
WHEN PURCHASE_AMOUNT > 10 AND PURCHASE_AMOUNT <= 20 THEN 20
WHEN PURCHASE_AMOUNT > 20 AND PURCHASE_AMOUNT <= 30 THEN 30 END AS PURCHASE_RANGE
FROM Table1
) AS A
GROUP BY PURCHASE_RANGE
ORDER BY PURCHASE_RANGE
SqlFiddle
try this
select PURCHASE_RANGE , NUM_OF_USERS
from (
select 1 as PURCHASE_RANGE ,count(*) as NUM_OF_USERS from table1 where PURCHASE_AMOUNT between 0 and 1
union all
select 5 ,count(*) from table1 where PURCHASE_AMOUNT between 1 and 5
union all
select 20 ,count(*) from table1 where PURCHASE_AMOUNT between 6 and 20
union all
select 30 ,count(*) from table1 where PURCHASE_AMOUNT between 21 and 30
)t
DEMO HERE
There are faster ways to do this if you need the performance (this will do a full table scan), but try this:
SELECT
SUM(CASE WHEN purchase_amount BETWEEN 0 AND 1 THEN 1 ELSE 0) bucket_0_to_1,
SUM(CASE WHEN purchase_amount BETWEEN 1 AND 5 THEN 1 ELSE 0) bucket_1_to_5,
SUM(CASE WHEN purchase_amount BETWEEN 5 AND 20 THEN 1 ELSE 0) bucket_5_to_20,
SUM(CASE WHEN purchase_amount BETWEEN 20 AND 30 THEN 1 ELSE 0) bucket_20_to_30,
SUM(CASE WHEN purchase_amount > 30 THEN 1 ELSE 0) bucket_over_30, FROM my_table LIMIT 1;
To get the values you want in rows, you need to start with a driver table that has all the values you are interested in, and then left outer join to the data:
select driver.mina, coalesce(sum(cnt), 0) as Num_Of_Users
from (select 1 as mina, 5 as maxa union all
select 5, 10 union all
select 10, 20 union all
select 20, 30 union all
select 30, NULL
) driver left outer join
(select purchase_amount, count(*) as cnt
from purchases
group by purchase_amount
) pa
on driver.mina >= pa.purchase_amount and
(pa.purchase_amount < driver.maxa or driver.maxa is null)
group by driver.mina
order by driver.mina
You can actually do this without the inner group by. That is likely to reduce the size of the data significantly (especially in your example) before join.
I would encourage you to include both the lower and upper bounds of the range on each row.
This might be easier if the ranges will ever change.
with ranges(rstart, rfinish) as (
select 0, 1 union all
select 2, 5 union all
select 6, 20 union all
select 21, 30
), purchases(amount) as (
select sum(PURCHASE_AMOUNT)
from <purchases_basetable> -- <-- your tablename goes here
group by USERID
)
select
-- concat(case when r.rstart = 0 then 0 else r.rstart-1 end, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* op's name for the group */,
concat(r.rstart, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* better name for the group */,
count(*) as NUM_OF_USERS
from
purchases as p inner join
ranges as r
on p.amount between r.start and r.finish
group by r.rstart, r.rfinish
order by r.rstart, r.rfinish
I don't know what the mysql query plan will look like. It's trivial to change the query to use derived tables rather than table expressions. (But I include it below anyway.)
You might also find the UNPIVOT operation to be useful on a platform that supports it.
select
-- concat(case when r.rstart = 0 then 0 else r.rstart-1 end, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* op's name for the group */,
concat(r.rstart, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* better name for the group */,
count(*) as NUM_OF_USERS
from
(
select sum(PURCHASE_AMOUNT) as amount
from <purchases_basetable> -- <-- your tablename goes here
group by USERID
) as p inner join
(
select 0 as rstart, 1 as rfinish union all
select 2, 5 union all
select 6, 20 union all
select 21, 30
) as r
on p.amount between r.start and r.finish
group by r.rstart, r.rfinish
order by r.rstart, r.rfinish

JIRA : Issue status count for the past x (i.e 30 ) days

With below Query I able to see the count(no) of issues for all issueType in JIRA for a given date .
ie.
SELECT count(*), STEP.STEP_ID
FROM (SELECT STEP_ID, ENTRY_ID
FROM OS_CURRENTSTEP
WHERE OS_CURRENTSTEP.START_DATE < '<your date>'
UNION SELECT STEP_ID, ENTRY_ID
FROM OS_HISTORYSTEP
WHERE OS_HISTORYSTEP.START_DATE < '<your date>'
AND OS_HISTORYSTEP.FINISH_DATE > '<your date>' ) As STEP,
(SELECT changeitem.OLDVALUE AS VAL, changegroup.ISSUEID AS ISSID
FROM changegroup, changeitem
WHERE changeitem.FIELD = 'Workflow'
AND changeitem.GROUPID = changegroup.ID
UNION SELECT jiraissue.WORKFLOW_ID AS VAL, jiraissue.id as ISSID
FROM jiraissue) As VALID,
jiraissue as JI
WHERE STEP.ENTRY_ID = VALID.VAL
AND VALID.ISSID = JI.id
AND JI.project = <proj_id>
Group By STEP.STEP_ID;
the result is
Status Count
open 12
closed 13
..... ....
What I'd like to achieve is something like this actually ..where the total count for status open and closed for each day .
Date COUNT(Open) COUNT(Closed)
12-1-2012 12 1
13-1-2012 14 5
The general strategy would be this:
Select from a table of all the days in a month
LEFT OUTER JOIN your table that gets counts for each day
(left outer join being necessary in case there were no entries for that day, you'd want it to show a zero value).
So I think this is roughly what you need (not complete and date-function syntax is probably wrong for your db, but it will get you closer):
SELECT aDate
, COALESCE(SUM(CASE WHEN IssueStatus = 'whateverMeansOpen' THEN 1 END,0)) OpenCount
, COALESCE(SUM(CASE WHEN IssueStatus = 'whateverMeansClosed' THEN 1 END,0)) ClosedCount
FROM
(
SELECT DATEADD(DAY, I, #START_DATE) aDate
FROM
(
SELECT number AS I FROM [SomeTableWithAtLeast31Rows]
where number between 1 and 31
) Numbers
WHERE DATEADD(DAY, I, #START_DATE) < #END_DATE
) DateTimesInInterval
LEFT OUTER JOIN
(
Put your query here. It needs to output two columns, DateTimeOfIssue and IssueStatus
) yourHugeQuery ON yourHugeQuery.DateTimeOfIssue BETWEEN aDate and DATEADD(DAY, 1, aDate)
GROUP BY aDate
ORDER BY aDate