Related
Output should be in below format, but I am getting wrong output:
Where 2019,2020,2021 column contains transaction done by customer in respectively 2019, 2020, 2021. Also if transactions in 2019,2020,2021 is equal Max_transaction is populated with first non-zero transaction year .
customer_name 2019 2020 2021 Max_transaction_year total_transaction
pug 2 1 0 2019 4
hari 0 1 1 2020 2
adh 0 0 1 2021 1
Sample table and data :
Also note that the first two digits in "tid" represent the year of transaction. Eg: 19597 -'19' represents 2019 and so on for 2020 and 2021.
create table client (cid int,cname char(10));
create table trans (tid int,cid int);
insert into client values(102,'pug'),(107,'ravi'),(109,'hari'),(105,'pon'),(106,'adh'),(104,'bav'),(101,'kat');
insert into trans values(19597,102),(19567,102),(20325,109),(21789,106),(17432,106),(21786,109),(20302,102),(17301,103);
Thanks in advance
Schema (MySQL v8.0)
create table client (cid int,cname char(10));
create table trans (tid int,cid int);
insert into client values(102,'pug'),(107,'ravi'),(109,'hari'),(105,'pon'),(106,'adh'),(104,'bav'),(101,'kat');
insert into trans values(19597,102),(19567,102),(20325,109),(21789,106),(17432,106),(21786,109),(20302,102),(17301,103);
Query #1
SELECT
customer_name,
SUM(
CASE WHEN year=2019 THEN no_transactions ELSE 0 END
) as '2019',
SUM(
CASE WHEN year=2020 THEN no_transactions ELSE 0 END
) as '2020',
SUM(
CASE WHEN year=2021 THEN no_transactions ELSE 0 END
) as '2021',
MAX(
CASE WHEN rn=1 THEN year ELSE 0 END
) as Max_transaction_year,
SUM(no_transactions) as total_transaction
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY customer_name ORDER BY no_transactions DESC) rn
FROM (
SELECT
c.cname as customer_name,
2000+FLOOR(tid / 1000) as year ,
COUNT(1) as no_transactions
FROM
trans t
INNER JOIN
client c ON t.cid = c.cid
WHERE
FLOOR(tid / 1000) BETWEEN 19 and 21
GROUP BY
c.cname, 2000+FLOOR(tid / 1000)
) p1
) p2
GROUP BY customer_name;
customer_name
2019
2020
2021
Max_transaction_year
total_transaction
adh
0
0
1
2021
1
hari
0
1
1
2020
2
pug
2
1
0
2019
3
View on DB Fiddle
I think a somewhat simpler method just uses conditional aggregation:
select cname, cnt_2019, cnt_2020, cnt_2021,
(case greatest(cnt_2019, cnt_2020, cnt_2021)
when cnt_2019 then '2019'
when cnt_2020 then '2020'
when cnt_2021 then '2021'
end) as max_year,
total_transactions
from (select c.cname, c.cid,
sum(year = '2019') as cnt_2019,
sum(year = '2020') as cnt_2020,
sum(year = '2021') as cnt_2021,
count(*) as total_transactions
from client c join
(select t.*, concat('20', left(t.tid, 2)) as year
from trans t
) t
on c.cid = t.cid
where year >= '2019' and year <= '2021'
group by c.cname, c.cid
) ct
order by cname, cid;
Here is a db<>fiddle.
I need to create a query that give me completed and unpaid transaction in 2019 grouped weekly. I already create the query to generate the completed transaction, but I stuck when try to combine the unpaid transaction query into the completed transaction query
This is the query for completed transaction
SELECT WEEK(A.plat_create_time, 1) AS 'Week Create Time',
COUNT(t1.lp_sign_time) AS 'Completed Order'
FROM deli_order A
LEFT JOIN
(
SELECT order_code, code, lp_sign_time
FROM pg_send_package
UNION
SELECT D.order_code, D.oms_code, C.cm_sign_time
FROM pg_package C
INNER JOIN pg_order D ON C.pg_order_id = D.id
) t1 ON t1.order_code = A.order_code
AND t1.code = A.code
AND YEAR(A.plat_create_time) = 2019
WHERE (YEAR(A.plat_create_time) = 2019) AND A.status = 6 AND t1.lp_sign_time IS NOT NULL
GROUP BY WEEK(A.plat_create_time, 1);
And its generate something like this
Week | Completed Order
1 886
2 734
3 868
4 1000
And this is the query that I already try to generate both completed and unpaid transaction
SELECT WEEK(A.plat_create_time, 1) AS 'Week Create Time',
COUNT(t1.lp_sign_time) AS 'Completed Order',
COUNT(t2.plat_create_time) AS 'Unpaid Order'
FROM deli_order A
LEFT JOIN
(
SELECT order_code, code, lp_sign_time
FROM pg_send_package
UNION
SELECT D.order_code, D.oms_code, C.cm_sign_time
FROM pg_package C
INNER JOIN pg_order D ON C.pg_order_id = D.id
) t1 ON t1.order_code = A.order_code
AND t1.code = A.code
AND YEAR(A.plat_create_time) = 2019
LEFT JOIN
(
SELECT order_code, WEEK(plat_create_time,1) AS 'Create Time'
FROM deli_order
WHERE pay_state = 0 AND (YEAR(plat_create_time) = 2019)
) t2 ON t2.order_code = A.order_code
WHERE (YEAR(A.plat_create_time) = 2019) AND A.status = 6 AND t1.lp_sign_time IS NOT NULL
GROUP BY WEEK(A.plat_create_time, 1);
but when I execute it, MySQL always give error message 'Unknown column 't2.plat_create_time' in field list'. My expected result is something like this
Week | Completed Order | Unpaid Order
1 886 51
2 734 42
3 868 40
4 1000 31
What should I change in my query?
Change:
SELECT order_code, WEEK(plat_create_time,1) AS 'Create Time'
to:
SELECT order_code, WEEK(plat_create_time,1) AS 'Create Time', plat_create_time
I have the following table snapshots:
domain year month day
--- --- --- ---
google 2007 04 15
google 2005 08 31
google 2005 12 01
facebook 2006 04 15
facebook 2006 02 25
facebook 2008 01 01
What I want to retrieve is the first (earliest) date of each domain.
So the output should be:
google 2005 08 31
facebook 2006 02 25
I have tried the following query, but it retrieves the minimum value for each column:
select domain, min(year), min(month), min(day) from snapshots group by domain
As mentioned you should use concatenation to create a single date and then select the lowest value.
select domain, MIN(CAST(CONCAT(`year`, '-'`,month`,'-',`day`) AS DATE)) from snapshots group by domain
Haven't tested this but this should give you an idea.
You can concatenate the values from the date field, cast them as date and select the min date (i expect the values to be varchar in this case):
SELECT domain,
MIN(CAST(CONCAT(year,'-',month,'-',day) AS date))
FROM snapshots
GROUP BY domain;
In MySQL:
SELECT
domain,
FROM_UNIXTIME(UNIX_TIMESTAMP(MIN(CONCAT(year,'-',month,'-',day))), '%Y') as y,
FROM_UNIXTIME(UNIX_TIMESTAMP(MIN(CONCAT(year,'-',month,'-',day))), '%m') as m,
FROM_UNIXTIME(UNIX_TIMESTAMP(MIN(CONCAT(year,'-',month,'-',day))), '%d') as d
FROM snapshots
GROUP BY domain;
There might be easier solutions, but you can create a new column of date type from the three columns year, month, and day. Then get the min date as following:
SELECT DISTINCT s.domain, s.year, s.month, s.day
FROM
(
SELECT domain, year,month,day,
STR_TO_DATE(CONCAT(`year`,'-',LPAD(`month`,2,'00'),'-',LPAD(`day`,2,'00')) ,'%Y-%m-%d') AS FullDate
FROM snapshots
) AS s
INNER JOIN
(
SELECT domain, MIN(Fulldate) MinDate
FROM
(
SELECT domain, year,month,day,
STR_TO_DATE(CONCAT(`year`,'-',LPAD(`month`,2,'00'),'-',LPAD(`day`,2,'00')) ,'%Y-%m-%d') AS FullDate
FROM snapshots
) AS t
GROUP BY domain
) AS t ON t.MinDate = s.FullDate
AND t.Domain = s.Domain;
demo
This will give you the exact results that you want:
| domain | year | month | day | MinDate |
|----------|------|-------|-----|------------|
| google | 2005 | 8 | 31 | 2005-08-31 |
| facebook | 2006 | 2 | 25 | 2006-02-25 |
Can you try this please and let me know if it solves your problem without concatenation? Could be made more robust with subqueries if necessary.
CREATE TABLE domainDate(domain CHAR(25), `year` INT, `month` INT, `day` INT);
INSERT INTO domainDate VALUES
('google', 2007, 04, 15),
('google', 2005, 08, 31),
('google', 2005, 12, 01),
('facebook', 2006, 04, 15),
('facebook', 2006, 02, 25),
('facebook', 2008, 01, 01);
SET #VDomain := '';
SELECT domain, `year`, `month`, `day` FROM domainDate HAVING #VDomain != #VDomain := domain ORDER BY domain, `year` * 10000 + `month` * 100 + `day`;
Thanks,
James
You can try ranking function ROW_NUMBER()
CREATE TABLE domainDate(domain CHAR(25), [year] INT, [month] INT, [day] INT);
INSERT INTO domainDate VALUES
('google', 2007, 04, 15),
('google', 2005, 08, 31),
('google', 2005, 12, 01),
('facebook', 2006, 04, 15),
('facebook', 2006, 02, 25),
('facebook', 2008, 01, 01);
SELECT domain
,[year]
,[month]
,[day]
FROM
(
SELECT domain
,[year]
,[month]
,[day]
,ROW_NUMBER() OVER(PARTITION BY domain ORDER BY [year], [month], [day]) AS RN
FROM domainDate
) t
WHERE RN = 1
Good morning, mysql query which displays cases that have a date field completed & a total row amount, I want calculate this against the total cases but unsure if I can do all this inside one query. For example the current query outputs
Aug Sep Nov Total
10 20 20 50
The code for that being
SELECT * from(
Select Count(b.CaseID) As TotDB
from tblcontacts a
Inner Join tblcases b
On a.ContactID = b.ContactAssignedTo)a
CROSS JOIN
(Select
Sum(Month(b.StatusSubmittedDate) = 8) As Aug,
Sum(Month(b.StatusSubmittedDate) = 9) As Sep,
Sum(Month(b.StatusSubmittedDate) = 10) As Oct,
Count(b.CaseID) As Total,
ROUND (100*Count(b.CaseID)/Count(b.CaseID),2) As Conversion
From
tblcontacts a Inner Join
tblcases b On a.ContactID = b.ContactAssignedTo
Where
b.StatusSubmittedDate > '2012 - 01 - 01'
Group By
a.ContactFullName With Rollup
Having
Sum(b.CaseCommission) > 0.01)b
What I need to it output is the below so I added the TotDB line above to see if that would help, it didn't. What I need to find out is can I have a column in this query that bypasses the where/having clause to display all records
Aug Sep Nov Tot TotDB %Converted
10 20 20 50 100 50%
Thanks
probably you should do like this:
select Aug,Sep, Nov, Tot,TotDB,(Tot/TotDB*1.0)*100 as '%Converted'
from
(SELECT * from(
Select Count(b.CaseDate) As TotDB
from tblcontacts a
Inner Join tblcases b
On a.ContactID = b.ContactAssignedTo)a
CROSS JOIN
(select
Sum(Month(b.StatusSubmittedDate) = 9) As Sep,
Sum(Month(b.StatusSubmittedDate) = 10) As Oct,
Sum(Month(b.StatusSubmittedDate) = 11) As Nov,
Count(b.CaseID) As Total,
From tblcontacts a
Inner Join tblcases b
On a.ContactID = b.ContactAssignedTo
Where
b.StatusSubmittedDate > '2012-01-01'
Group By
a.ContactFullName With Rollup
Having
Sum(b.CaseCommission) > 0.01)b)c
You cannot do that in one simple query.
The right way to do that, is to do a second query, without the where clause. Really.
If you MUST do this in one query, atleast use a union:
/* old query */
SELECT a,b,c from t1,t2,t3 where d=e group by a having b>f
UNION
select 'total',COUNT(*),NULL /*need the same amount of rows*/
from t1,t2,t3
And have your clientside treat the row where a='total' differently. But that's a hack. I would advise you to use two queries.
I have 3 columns doc_date, chem_date, st_date
all 3 columns belong to different tables:
doc
doc_date count(x)
01-02-2012 2
02-02-2012 3
chem
chem_date count(x)
04-02-2012 1
06-02-2012 0
stock
st_date count(x)
01-02-2012 1
03-02-2012 5
I want to write select clause like this
case doc_date
when '01' then count(x),
case chem_date
when '01' then count(x),
case st_date
when '01' then count(x)
end as '01',
case doc_date
when '02' then count(x),
case chem_date
when '02' then count(x),
case st_date
when '02' then count(x)
end as '02',
....up to 31 days
If some case statements have an entry on same date e.g if doc_date and st_date both exist on '01-02-2012' then their respective count should be added to the final count(x) means count(x) + count(x)
So, the answer should be:
01 02 03 04 05 06 07 08 09 10 11 12.. up to 31 days
3 3 5 1 0 count(x) value for particular date
Explanation of the output: Here, the starting value for date 01 is 2 for doc table and for stock table the value is 1. So, the final value will become addition of them which is 3
The same rule will be applicable for others.
Any suggestions how can I achieve this output in SQL? Or any other way?
Here's a pseudo code showing how it can be done:
select
case when date_format(doc_date,"%d") = '01' then sum_x else null end as '01',
case when date_format(doc_date,"%d") = '02' then sum_x else null end as '02',
--and etc
from (
select doc_date, sum(doc_x) as sum_x --sum the x value
from
(
select doc_date,doc_x --combine all 3 table stuff
from doc a
union all
select chem_date,chem_x
from chem a
union all
select st_date,st_x
from st a
) h
group by doc_date --use first col name as the union's col name
) g
limit 1,1
If the doc, chem and st are combined using joins and have different schema, you may need to do pivot, either using multiple join to itself or use case... when...:
select
case when date_format(doc_date,"%d") = '01' then sum_x else null end as '01',
case when date_format(doc_date,"%d") = '02' then sum_x else null end as '02',
--and etc
from (
select doc_date,
a_x + b_x + c_x as sum_x --sum them according to date
from (select ... from doc where ...) a
left join (select ... from chem where ...) b on a.doc_date=b.doc_date and --some join condition
left join (select ... from st where ...) c on a.doc_date=c.doc_date and --some join condition
) g
limit 1,1
I am working with an ambiguous question here, so clarify as needed,