Add string to mysql result - mysql

I need mySql result with some prefixes and suffixes. This is my code:
SELECT bk_id, bk_rtype, villas_db.v_name AS villa_name
FROM booking_db
INNER JOIN villas_db ON booking_db.bk_vid = villas_db.v_id
WHERE '2012-11-02'
BETWEEN bk_date1
AND bk_date2
ORDER BY bk_id DESC
LIMIT 0 , 30
The result are:
bk_id bk_rtype villa_name
30 2 T2
29 3 V1
So I need the result as:
bk_id bk_rtype villa_name booking_no
30 2 T2 B2-00030
29 3 V1 B3-00029
While B is a booking prefix separate by "-" and sprintf("%05d",bk_id);
Please suggest.

You may want to use CONCAT and LPAD functions as below:
SELECT bk_id, bk_rtype, villas_db.v_name AS villa_name,
CONCAT('B',bk_rtype,'-', LPAD(bk_id, 5, '0')) AS booking_no
FROM booking_db
INNER JOIN villas_db ON booking_db.bk_vid = villas_db.v_id
WHERE '2012-11-02'
BETWEEN bk_date1
AND bk_date2
ORDER BY bk_id DESC
LIMIT 0 , 30 ;

I found my solution:
SELECT bk_id, bk_rtype, villas_db.v_name AS villa_name, concat( 'B', bk_rtype, '-', lpad( bk_id, 5, 0 ) ) AS booking_no
FROM booking_db
INNER JOIN villas_db ON booking_db.bk_vid = villas_db.v_id
WHERE '2012-11-02'
BETWEEN bk_date1
AND bk_date2
ORDER BY bk_id DESC
LIMIT 0 , 30

Related

MySQL duration for 9 rows, that's my code

SELECT DATE_FORMAT(b.checktime,"%Y-%m-%d") AS dtDate,
case when (
SELECT DATE_FORMAT(a.checktime,'%H:%i:%s')
FROM checkinout a
WHERE a.checktype='0'
and a.userid=1
and DATE_FORMAT(a.checktime,'%Y-%m-%d')=DATE_FORMAT(b.checktime,'%Y-%m-%d')
LIMIT 1)
is not NULL
then
(
SELECT
DATE_FORMAT(a.checktime,'%H:%i:%s')
FROM checkinout a
WHERE a.checktype='0'
and a.userid=1
and DATE_FORMAT(a.checktime,'%Y-%m-%d')=DATE_FORMAT(b.checktime,'%Y-%m-%d')
LIMIT 1
)
else 'N/A' END
AS Chkin
from checkinout b
where
DATE_FORMAT(b.checktime,"%m")='06' AND
DATE_FORMAT(b.checktime,"%Y")='2019' AND
DATE_FORMAT(b.checktime,"%w")!='0' AND .
DATE_FORMAT(b.checktime,"%w")!='6' AND .
DATE_FORMAT(b.checktime,"%Y-%m-%d") not in
(select date_s from wend)
GROUP BY dtDate
ORDER BY dtDate DESC
Affected rows: 0 Found rows: 9 Warnings: 0 Duration for 1 query: 25.665 sec.
what's wrong with this query, for 9 rows need 25.665 seconds
Thanks
Without understanding the purpose of the query, it can be rewritten to:
SELECT DISTINCT date(b.checktime) AS dtDate,
coalesce((
SELECT time(a.checktime)
FROM checkinout a
WHERE a.checktype = '0'
and a.userid = 1
and date(a.checktime) = date(b.checktime)
LIMIT 1
), 'N/A') AS Chkin
from checkinout b
where month(b.checktime) = 6
AND year(b.checktime) = 2019
AND weekday(b.checktime) not in (5, 6) -- non weekend
AND date(b.checktime) not in (select date_s from wend)
ORDER BY dtDate DESC
Though GROUP BY might perform better than DISTINCT - You should test both. However, the above query can be optimized to use indexes.
The outer condition month(b.checktime) = 6 AND year(b.checktime) = 2019 can be rewritten to b.checktime >= '2019-06-01' AND b.checktime < '2019-06-01' + INTERVAL 1 MONTH. This way the engine should be able to use an index on (checktime).
The condition date(a.checktime) = date(b.checktime) can be written as a.checktime >= date(b.checktime) and a.checktime < date(b.checktime) + INTERVAL 1 DAY. Here I suggest one of the following indexes: (checktype, userid, checktime), (userid, checktype, checktime) or (userid, checktime).
And for date(b.checktime) not in (select date_s from wend) it might be better to use an "anti-join".
So here is the final query, which I would try:
SELECT DISTINCT date(b.checktime) AS dtDate,
coalesce((
SELECT time(a.checktime)
FROM checkinout a
WHERE a.checktype = '0'
and a.userid = 1
and a.checktime >= date(b.checktime)
and a.checktime < date(b.checktime) + INTERVAL 1 DAY
LIMIT 1
), 'N/A') AS Chkin
from checkinout b
LEFT JOIN wend w ON w.date_s = date(b.checktime)
where b.checktime >= '2019-06-01'
AND b.checktime < '2019-06-01' + INTERVAL 1 MONTH
AND weekday(b.checktime) not in (5, 6) -- non weekend
AND w.date_s IS NULL
ORDER BY dtDate DESC

How to convert this SQL-Statement to PostgreSQL?

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;

mysql address outer query from subquery

I have this query:
SELECT
sec_to_time(avg(t1.sessiontime)) as aloc,
CONCAT(TRUNCATE(sum(t1.terminatecauseid = 1) * 100 / count(*),
1),
'%') as asr,
count(*) as calls,
cast(t1.destination as unsigned) as prefix,
t2.destination as destination,
SEC_TO_TIME(sum(t1.sessiontime)) as duration
FROM
cc_call AS t1
inner join
cc_prefix as t2 ON t1.destination = t2.prefix
WHERE
t1.card_id = '133' AND t1.starttime >= ('2014-06-1') AND t1.starttime <= ('2014-07-01 23:59:59') and t1.terminatecauseid = 1
group by t1.destination
order by duration DESC
LIMIT 0 , 25
t1.terminatecauseid = 1 means successful call,
'asr' means average success rate,
Im trying to find out how many calls with (t1.terminatecauseid = 1) from the total calls made to an extension.
this line doesn't work:
sum(t1.terminatecauseid = 1) * 100 / count(*)
since I already have (t1.terminatecauseid = 1) in the WHERE clause.
Im thinking about putting a subquery, to retrieve total calls, where count(*) currently is.
How can I have this query calculate the ASR with total calls made?
example sqlfiddle
if possible, I'd like to not show results with duration=NULL
Use conditional aggregation, something like this:
SELECT sec_to_time(avg(case when t1.terminatecauseid = 1 then t1.sessiontime end)) as aloc,
CONCAT(TRUNCATE(sum(t1.terminatecauseid = 1) * 100 / count(*),
1),
'%') as asr,
count(*) as TotalCalls,
sum(t1.terminatecauseid = 1) as Terminated1Calls,
cast(t1.destination as unsigned) as prefix,
t2.destination as destination,
SEC_TO_TIME(sum(case when t1.terminatecauseid = 1 then t1.sessiontime end)) as duration
FROM cc_call t1 inner join
cc_prefix t2
ON t1.destination = t2.prefix
WHERE t1.card_id = '133' AND
t1.starttime >= ('2014-06-1') AND t1.starttime <= ('2014-07-01 23:59:59')
group by t1.destination
order by duration DESC
LIMIT 0 , 25;

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

SQL debug - where condition ignored

Can somebody explain me this. My SQL:
SELECT
`offers`.`id`,
`offers`.`max_available`,
(SELECT COUNT( coupons.id ) FROM coupons WHERE coupons.status = 'Y' AND coupons.offer_id = offers.id) AS coupons_sold
FROM
`offers`
WHERE
`offers`.`status` IN ('P', 'S') AND
`offers`.`published_at` < 1341612000 AND
`offers`.`end_at` >1341567914 AND
`coupons_sold` < `offers`.`max_available`
ORDER BY `offers`.`created_at` DESC
LIMIT 4 OFFSET 0
This will return me these 4 rows:
id max_available coupons_sold
195 19 20
194 9999 0
193 9999 0
159 9999 93
How is possible that row with ID 195 is included, if I have this condition in where coupons_sold < offers.max_available? I am clueless!
This query would produce an error, as you can't use in WHERE clause, an alias from the SELECT list. Unless table offers has a coupons_sold column, too!
Try this query, instead:
SELECT id, max_available, coupons_sold
FROM
( SELECT
`offers`.`id`,
`offers`.`max_available`,
( SELECT COUNT( coupons.id )
FROM coupons
WHERE coupons.status = 'Y'
AND coupons.offer_id = offers.id
) AS coupons_sold
offers.created_at
FROM
`offers`
WHERE
`offers`.`status` IN ('P', 'S') AND
`offers`.`published_at` < 1341612000 AND
`offers`.`end_at` >1341567914
) AS tmp
WHERE coupons_sold < max_available
ORDER BY created_at DESC
LIMIT 4 OFFSET 0 ;