Mysql adding new columns for the selection - mysql

I have the following table
+-----+------------------+-------------+
| id | name |month_1 |
+-----+------------------+-------------+
| 1 | anna | 15 |
| 2 | bin | 20 |
+-----+------------------+-------------+
When I make a selection I want to add one more column.
For example
SELECT id,name, money as month_1 FROM test where month(day)='1';
And I want to add a column, something like this:
SELECT id,name, money as month_1,money as month_2
FROM test
where where month(day)='1',where month(day)='2'
+-----+------------------+-------------+------------+
| id | name | month_1 |month_2 |
+-----+------------------+-------------+------------+
| 1 |anna | 15 | 10 |
| 2 | bin | 20 | 0 |
+-----+------------------+-------------+------------+

You can use conditional aggregation:
SELECT id,name,
SUM(CASE WHEN month(day) = 1 THEN money ELSE 0 END) as month_1,
SUM(CASE WHEN month(day) = 2 THEN money ELSE 0 END) as month_2
FROM test
GROUP BY id, name;
You may or may not want to include the month and year in the aggregation.

Related

How to count in a range of result in mysql

l have a record table now, and l must to statistics the result of every month.
here is a test table
+----+------+----------+----------+------+
| id | name | grade1 | grade2 | time |
+----+------+----------+----------+------+
| 1 | a | 1 | 1 | 1 |
| 2 | a | 0 | 1 | 1 |
| 3 | a | 1 | 2 | 2 |
| 4 | b | 1 | 2 | 2 |
| 5 | a | 1 | 1 | 2 |
+----+------+----------+----------+------+
5 rows in set (0.01 sec)
time column means month(the actual is timestamp).
l need to statistics total number those grade1 >=1 && grade2 >=1 in every month
So, l want to get the result like this
+----+------+----------+----------+----------+----------+------+
| id | name | grade1_m1| grade2_m1| grade1_m2| grade2_m2| time |
+----+------+----------+----------+----------+----------+------+
| 13 | a | 1 | 2 | null | null | 1 |
| 14 | a | null | null | 2 | 2 | 2 |
| 15 | b | null | null | 1 | 1 | 2 |
+----+------+----------+----------+----------+----------+------+
3 rows in set (0.00 sec)
fake code of sql seem like this:
select
count(grade1 where time=1 and grade1 >= 1) as grade1_m1,
count(grade2 where time=1 and grade2 >= 1) as grade1_m1,
count(grade1 where time=2 and grade1 >= 1) as grade1_m2,
count(grade2 where time=2 and grade2 >= 1) as grade1_m2,
-- ... 12 months' statistics
from test
group by name
In the fact, l done it, but with temporary table like follow:
select
count(if(m1.grade1>=1, 1, null)) as grade1_m1,
count(if(m1.grade2>=1, 1, null)) as grade2_m1,
count(if(m2.grade1>=1, 1, null)) as grade1_m2,
count(if(m2.grade2>=1, 1, null)) as grade2_m2,
-- ...
from test
left join
(select * from test where time = 1) as m1
on m1.id = test.id
left join
(select * from test where time = 1) as m2
on m2.id = test.id
-- ...
group by name
But this sql is toooooooo long. this test table is just a simple version. Under real situation, l printed my sql and that took up two screens in chrome. So l am seeking a more simple way to complete it
You're original version is almost there. You need case and sum() is more appropriate:
select name,
sum(case when time=1 and grade1 >= 1 then grade1 end) as grade1_m1,
sum(case when time=1 and grade2 >= 1 then grade2 end) as grade2_m1,
sum(case when time=2 and grade1 >= 1 then grade1 end) as grade1_m2,
sum(case time=2 and grade2 >= 1 then grade2 end) as grade2_m2,
-- ... 12 months' statistics
from test
group by name

Join two table and count, avoid zero if record is not available in second table

I have following tables products and tests.
select id,pname from products;
+----+---------+
| id | pname |
+----+---------+
| 1 | prd1 |
| 2 | prd2 |
| 3 | prd3 |
| 4 | prd4 |
+----+---------+
select pname,testrunid,testresult,time from tests;
+--------+-----------+------------+-------------+
| pname | testrunid | testresult | time |
+--------+-----------+------------+-------------+
| prd1 | 800 | PASS | 2017-10-02 |
| prd1 | 801 | FAIL | 2017-10-16 |
| prd1 | 802 | PASS | 2017-10-02 |
| prd1 | 803 | NULL | 2017-10-16 |
| prd1 | 804 | PASS | 2017-10-16 |
| prd1 | 805 | PASS | 2017-10-16 |
| prd1 | 806 | PASS | 2017-10-16 |
+--------+-----------+------------+-------------+
I like to count test results for products and if there is no result available,for a product just show a zero for it. something like following table:
+--------+------------+-----------+----------------+---------------+
| pname | total_pass | total_fail| pass_lastweek | fail_lastweek |
+--------+------------+-----------+----------------+---------------+
| prd1 | 5 | 1 | 3 | 1 |
| prd2 | 0 | 0 | 0 | 0 |
| prd3 | 0 | 0 | 0 | 0 |
| prd4 | 0 | 0 | 0 | 0 |
+--------+------------+-----------+----------------++--------------+
I have tried different queries like following, which is just working for one product and is incomplete:
SELECT pname, count(*) as pass_lastweek FROM tests where testresult = 'PASS' AND time
>= '2017-10-11' and pname in (select pname from products) group by pname;
+-------------+---------------+
| pname | pass_lastweek |
+-------------+---------------+
| prd1 | 3 |
+-------------+---------------+
it looks so basic but still I am unable to write it, any idea?
Use conditional aggregation. The COUNT function count NULL values as zeros automatically, therefore, there is no need to take care of that.
select p.pname,
count(case when testresult = 'PASS' then 1 end) as total_pass,
count(case when testresult = 'FAIL' then 1 end) as total_fail,
count(case when testresult = 'PASS' and time >= curdate() - INTERVAL 6 DAY then 1 end) as pass_lastweek ,
count(case when testresult = 'FAIL' and time >= curdate() - INTERVAL 6 DAY then 1 end) as fail_lastweek ,
from products p
left join tests t on t.pname = p.pname
group p.id, p.pname
Generally, you need to LEFT JOIN the first table with the second one before you group. The join will give you a row for each product (even if there are no test results to join it to; INNER JOIN would exclude products with no associated tests) + an additional row for each test result (beyond the first). Then you can group them.
SELECT products.*, tests.* FROM products
LEFT JOIN tests ON products.pname = tests.pname
GROUP BY products.id
Also, I would strongly recommend using a product_id column in the tests table, rather than using pname (if a products.pname changes, your whole DB breaks unless you also update the pname field in kind for every test result). The general query would then look like this:
SELECT products.*, tests.* FROM products
LEFT JOIN tests ON products.id = tests.product_id
GROUP BY products.id
I used 2 queries , the first with conditional count and the second one is to change all null values into 0 :
select pname,
case when total_pass is null then 0 else total_pass end as total_pass,
case when total_fail is null then 0 else total_fail end as total_fail,
case when pass_lastweek is null then 0 else pass_lastweek end as pass_lastweek,
case when fail_lastweek is null then 0 else fail_lastweek end asfail_lastweek from (
select products.pname,
count(case when testresult = 'PASS' then 1 end) as total_pass,
count(case when testresult = 'FAIL' then 1 end) as total_fail,
count(case when testresult = 'PASS' and time >= current_date -7 DAY then 1 end) as pass_lastweek ,
count(case when testresult = 'FAIL' and time >= current_date -7 DAY then 1 end) as fail_lastweek ,
from products
left join tests on tests.pname = products.pname
group 1 ) t1

MySQL Select Case - Comparison Between Two Tables

I have two tables similar to these:
Table: case
---------------------------------------------------
| id | company | managed | time | client |
---------------------------------------------------
| 1 | apple | yes | 1412643785 | no |
---------------------------------------------------
| 2 | barilla | no | 1412643785 | no |
---------------------------------------------------
| 3 | google | no | 1412643785 | yes |
---------------------------------------------------
| 4 | google | yes | 1412643785 | yes |
---------------------------------------------------
| 5 | google | no | 1412643785 | yes |
---------------------------------------------------
Table: language
---------------------------
| id | company | lang |
---------------------------
| 1 | apple | EN |
---------------------------
| 2 | barilla | IT |
---------------------------
| 3 | google | EN |
---------------------------
I have create statistics/graphics from this tablet, the I extract the following information for each month:
* Number of cases of clients per month
* Number of cases managed per month
* Total number of cases per month
* Number of cases of Italian companies per month
For the first three points I don't have no problems, and I have made this query:
SELECT FROM_UNIXTIME(time, '%Y-%M') as 'Month',
COUNT(CASE WHEN client = 'yes' THEN 1 ELSE NULL END) as 'Reports of a customer,
COUNT(CASE WHEN managed = 'yes' THEN 1 ELSE NULL END) as 'Managed cases',
COUNT(id) as 'Total reports'
FROM case
GROUP BY FROM_UNIXTIME(time, '%Y-%M')
ORDER BY FROM_UNIXTIME(time, '%Y-%m')
But how do I extract the monthly number of cases managed of any Italian company?
I tried to add this portion of query but does not go...
COUNT(CASE WHEN case.company = language.company AND language.lang = 'IT' THEN 1 ELSE NULL END) as 'Italian Case',
Can you help me? Thank you
Your thought was right, just needed to join the additional table:
SELECT FROM_UNIXTIME(t.time, '%Y-%M') as 'Month',
COUNT(CASE WHEN t.client = 'yes' THEN 1 END) as 'Reports of a customer',
COUNT(CASE WHEN t.managed = 'yes' THEN 1 END) as 'Managed cases',
COUNT(t.id) as 'Total reports',
COUNT(CASE WHEN language.lang = 'IT' THEN 1 END) as 'Italian Case',
FROM case t
JOIN language ON language.company = t.company
GROUP BY FROM_UNIXTIME(t.time, '%Y-%M')
ORDER BY FROM_UNIXTIME(t.time, '%Y-%m')
Please note that I removed ELSE NULL from your cases as it is the default.

How to do one big select from table in MySQL?

I need to show the data from DB into a table of report file.
my_table looks like:
+----+-------+------+------+-------------------+-----------+-------+----+-------------------+
| id |entryID|userID|active| dateCreated |affiliateId|premium|free| endDate |
| 1 | 69856 | 1 | N |2014-03-22 13:54:49| 1 | N | N |2014-03-22 13:54:49|
| 2 | 63254 | 2 | Y |2014-03-21 13:35:15| 2 | Y | N | |
| 3 | 56324 | 3 | N |2014-03-21 11:11:22| 2 | Y | N |2014-02-22 16:44:46|
| 4 | 41256 | 4 | Y |2014-03-21 08:10:46| 1 | N | Y | |
| .. | ... | ... | ... | ... | ... | ... | .. | ... |
+----+-------+------+------+-------------------+-----------+-------+----+-------------------+
I need to create the table with data from my_table
| Date | № of Entries (in that date) | Total № of Entries | Premium | Free | Afiiliate |
The final table in file should looks like:
Report 17-07-2013:
+----------+--------------+-------+---------+------+-----------+
| Date | № of Entries | Total | Premium | Free | Afilliate |
|2013-07-17| 2 | 99845 | 2 | 0 | 0 |
|2013-07-18| 1 | 99843 | 0 | 1 | 0 |
|2013-07-22| 1 | 99842 | 1 | 0 | 1 |
|2013-07-23| 3 | 99841 | 2 | 1 | 2 |
|2013-07-24| 298 | 99838 | 32 | 273 | 25 |
|2013-07-25| 5526 | 99540 | 474 | 5058 | 126 |
|2013-07-26| 1686 | 94014 | 157 | 1532 | 56 |
|2013-07-27| 1673 | 92328 | 156 | 1517 | 97 |
|2013-07-28| 1461 | 90655 | 155 | 1310 | 83 |
| ... | ... | ... | ... | ... | ... |
+----------+--------------+-------+---------+------+-----------+
Should I for each column do a SELECT or I should do only 1 select?
If it possible to do 1 select how to do it?
It should be by analogy with this report:
report
Some fields differ (like 'Number of Entries in that date').
Total number of Entries means: all entries from beginning to the that specific date.
Number of Entries in that date means: all entries in that date.
In a final table the date from column Date will not repeat, that's why Column 'Number of Entries (in that date)' will calculate all entries for that date.
Your result is not so clear for the total is a count or sum and affiliate is sum or count also
but assuming total will be count and affiliate will be sum
here a query you might use to give you a result ( using ms-sql )
select DateCreated,count(EntryId) as Total,
sum(case when Premium='Y' then 1 else 0 end) as Premium,
sum(case when Premium='N' then 1 else 0 end) as Free,
sum(AffiliateId) as Affiliate
from sample
group by DateCreated
here a working demo
if I didn't understood you correctly, kindly advise
hope it will help you
SQLFiddle Demo: http://sqlfiddle.com/#!9/20cc0/5
The added column entryID does not matter for us.
I don't really understand what you want for Total, or the criteria for affiliateID. This query should get you started.
SELECT
DATE(dateCreated) as "Date",
count(dateCreated) as "No of Entries",
99845 as Total,
sum( case when premium='Y' then 1 else 0 end ) as Premium,
sum( case when premium='N' then 1 else 0 end ) as Free,
sum( case when affiliateID IS NOT NULL then 1 else 0 end) as Affiliate
FROM MyTable
GROUP BY DATE(dateCreated)
ORDER BY Date ASC
The final table in file should looks like:
... This new table can be in a file or in the web page. But it is not a new table in DB. –
It sounds like you may be new to this area so I just wanted to inform you that spitting out a report into a file for a website is highly unusual and typically only done when your data is completely separate from the website. Putting data from a database onto a website (like the query we made here) is very common and it's very likely you don't need to mess with any files.
select date(DateCreated),count(entryId) as Total,
sum(case when Premium='Y' then 1 else 0 end) as Premium,
sum(case when Premium='N' then 1 else 0 end) as Free,
sum( case when affiliateID IS NOT NULL then 1 else 0 end) as Affiliate
INTO OUTFILE '/tmp/myfile.csv'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
from my_table
group by date(DateCreated) order by date(DateCreated);

Collecting multiple columns of aggregate data with a join

I'm trying to figure out if the query I'd like to do is at all doable or feasible in SQL or if I need to collect raw data and process it in my application.
My schema looks like this:
applications
================
id INT
application_steps
=================
id INT
application_id INT
step_id INT
activated_at DATE
completed_at DATE
steps
=====
id INT
step_type_id INT
Ideally, with this data in application_steps:
| id | application_id | step_id | activated_at | completed_at |
| 1 | 1 | 1 | 2013-01-01 | 2013-01-02 |
| 2 | 1 | 2 | 2013-01-02 | 2013-01-02 |
| 3 | 1 | 3 | 2013-01-02 | 2013-01-10 |
| 4 | 1 | 4 | 2013-01-10 | 2013-01-11 |
| 5 | 2 | 1 | 2013-02-02 | 2013-02-02 |
| 6 | 2 | 2 | 2013-02-02 | 2013-02-07 |
| 7 | 2 | 4 | 2013-02-09 | 2013-02-11 |
I want to get this result:
| application_id | step_1_days | step_2_days | step_3_days | step_4_days |
| 1 | 1 | 0 | 8 | 1 |
| 2 | 0 | 5 | NULL | 2 |
Note that in reality there are many more steps and many more applications that I would be looking at.
As you can see, there is a has-many relation between applications and application_steps. It is also possible for a given step to not be in use for a particular application. I'd like to get the amount of time each step takes (using DATEDIFF(completed_at, activated_at)), all in one row (the column names don't matter). Is this at all possible?
Secondary question: To complicate things a bit further, I will also need a secondary query which joins application_steps with steps and only gets data for steps with a particular step_type_id. Assuming part one is possible, how can I extend it to filter efficiently?
NOTE: Efficiency is key here - this is for a yearly report, which equates to about 2500 applications with 70 different steps and 44,000 application_steps in production (not a lot of data, but potentially a lot when joins are factored in).
This should be a basic "pivoting" aggregation:
select id,
max(case when step_id = 1 then datediff(completed_at, activated_at) end) as step_1_days,
max(case when step_id = 2 then datediff(completed_at, activated_at) end) as step_2_days,
max(case when step_id = 3 then datediff(completed_at, activated_at) end) as step_3_days,
max(case when step_id = 4 then datediff(completed_at, activated_at) end) as step_4_days
from application_steps s
group by id;
You would have to repeat this for all 70 steps.
To do this only for a particular type of step:
select application_id,
max(case when step_id = 1 then datediff(completed_at, activated_at) end) as step_1_days,
max(case when step_id = 2 then datediff(completed_at, activated_at) end) as step_2_days,
max(case when step_id = 3 then datediff(completed_at, activated_at) end) as step_3_days,
max(case when step_id = 4 then datediff(completed_at, activated_at) end) as step_4_days
from application_steps s join
steps
on s.step_id = steps.id and
steps.step_type_id = XXX
group by application_id;