How can you Select two columns and have each column test for it's own condition and not the other's ?
Let's say I have a select that Count every records in a table. In one column I want every records from this week, and in the second one I want all record since the beginning of the year.
I have two conditions but they each apply to a specific column :
WHERE date BETWEEN #Monday AND #SUNDAY /* Weekly */
WHERE date >= #JanuaryFirst /* Annual */
But can't just put it like this because I will only get this week's record in both columns. I thought I could use an IFcondition but I don't think I can simply say "If you are column A test for this, if not test for the second one".
Here is a version that doesn't yield multiple scans:
select vehicule,
weekly = SUM(CASE WHEN date BETWEEN #Monday AND #SUNDAY THEN 1 ELSE 0 END),
annual = SUM(CASE WHEN date >= #JanuaryFirst THEN 1 ELSE 0 END)
from dbo.tablename AS t
GROUP BY vehicule;
Or you could also try the slightly less verbose:
select vehicule,
weekly = COUNT(CASE WHEN date BETWEEN #Monday AND #SUNDAY THEN 1 END),
annual = COUNT(CASE WHEN date >= #JanuaryFirst THEN 1 END)
from dbo.tablename AS t
GROUP BY vehicule;
Use INNER SELECTS, like this:
select vehicule,
(select count(*) from tablename t1 where t1.vehicule = t.vehicule and date BETWEEN #Monday AND #SUNDAY) as 'Weekly',
(select count(*) from tablename t1 where t1.vehicule = t.vehicule and date >= #JanuaryFirst) as 'Annual'
from tablename t
If you want to avoid subqueries you can use:
select vehicule,
sum(case when date BETWEEN #Monday AND #SUNDAY then 1 else 0 end) as 'Weekly',
sum(case when date >= #JanuaryFirst then 1 else 0 end) as 'Annual'
group by vehicule
Related
I am working with a query where I want to display number of upcoming dates. The following query returns 0 even though there are dates greater than current date. Please help me to solve this problem.
SELECT (case when b.booked_date > cast(now() as date) then sum(1) else sum(0) end) as upcoming_booked_facilities
from svk_apt_book_facilities b
where b.customer_id = 1
and b.association_id = 1
and b.is_active = 1
group by b.facility_id
You need to sum a CASE expression to do conditional aggregation:
SELECT
facility_id,
SUM(CASE WHEN booked_date > CURDATE() THEN 1 ELSE 0 END) AS upcoming_booked_facilities
FROM svk_apt_book_facilities
WHERE
customer_id = 1 AND
association_id = 1 AND
is_active = 1
GROUP BY
facility_id;
You were trying to use the sum as the predicate of the CASE expression, which is probably not what you want. Note that I am also selecting the facility_id, since you are grouping by that column. If you instead want a conditional sum over the entire table, then don't select or group by facility.
Fairly new to SQL but I'm trying to get the difference between 2 select queries from the same Table. I have tried the following
SELECT
(SELECT KwhMeter,IndexElek,CalorieMeter,IndexWarmte,IndexWarmWater,IndexKoudWater,Date FROM Energiemeters WHERE Date = '2017-05-01')
-
(SELECT KwhMeter,IndexElek,CalorieMeter,IndexWarmte,IndexWarmWater,IndexKoudWater,Date FROM Energiemeters WHERE Date = '2017-04-01') AS Difference
but I end up having the following error :
#1241 - Operand should contain 1 column(s)
If you want rows that are on May 1st but not April 1st, then one way is to use aggregation:
SELECT KwhMeter, IndexElek, CalorieMeter, IndexWarmte, IndexWarmWater, IndexKoudWater, Date
FROM Energiemeters
WHERE Date IN ('2017-04-01', '2017-05-01')
GROUP BY KwhMeter, IndexElek, CalorieMeter, IndexWarmte, IndexWarmWater, IndexKoudWater
HAVING MIN(Date) = '2017-05-01';
Using Cross Join. This is with the assumption that you get only 1 row per date.
Select
(a.KwhMeter-b.KwhMeter) as KwhMeter,
(a.IndexElek-b.IndexElek) as IndexElek,
(a.CalorieMeter-b.CalorieMeter) CalorieMeter,
(a.IndexWarmte-b.IndexWarmte) IndexWarmte,
(a.IndexWarmWater-b.IndexWarmWater) IndexWarmWater,
(a.IndexKoudWater-b.IndexKoudWater) IndexKoudWater,
(a.Date-b.Date) as Date
from
(
SELECT distinct KwhMeter,IndexElek,CalorieMeter,IndexWarmte,IndexWarmWater,IndexKoudWater,Date
FROM Energiemeters
WHERE Date = '2017-05-01'
) a
cross join
(
SELECT distinct KwhMeter,IndexElek,CalorieMeter,IndexWarmte,IndexWarmWater,IndexKoudWater,Date
FROM Energiemeters
WHERE Date = '2017-04-01'
) b;
It seems as though you want to subtract the respective values of columns from two rows determined by dates 2017-05-01 and 2017-04-01?
If yes, then the query can be written as follows:
SELECT SUM(CASE Date
WHEN '2017-05-01' THEN KwhMeter
WHEN '2017-04-01' THEN -KwhMeter
END) AS KwhMeter,
SUM(CASE Date
WHEN '2017-05-01' THEN IndexElek
WHEN '2017-04-01' THEN -IndexElek
END) AS IndexElek,
SUM(CASE Date
WHEN '2017-05-01' THEN CalorieMeter
WHEN '2017-04-01' THEN -CalorieMeter
END) AS CalorieMeter,
SUM(CASE Date
WHEN '2017-05-01' THEN IndexWarmte
WHEN '2017-04-01' THEN -IndexWarmte
END) AS IndexWarmte,
SUM(CASE Date
WHEN '2017-05-01' THEN IndexWarmWater
WHEN '2017-04-01' THEN -IndexWarmWater
END) AS IndexWarmte,
SUM(CASE Date
WHEN '2017-05-01' THEN IndexKoudWater
WHEN '2017-04-01' THEN -IndexKoudWater
END) AS IndexKoudWater
FROM Energiemeters
WHERE Date IN ('2017-05-01', '2017-04-01')
A small scale working demo can be found here.
WORKING IN MOST RDBMSs EXCEPT MYSQL:
If I wanted to compute per-column difference I would use common table expressions to prepare subresults and then compute difference.
WITH
res1 AS
(SELECT KwhMeter,IndexElek,CalorieMeter,IndexWarmte,IndexWarmWater,IndexKoudWater,Date FROM Energiemeters WHERE Date = '2017-05-01'),
res2 AS
(SELECT KwhMeter,IndexElek,CalorieMeter,IndexWarmte,IndexWarmWater,IndexKoudWater,Date FROM Energiemeters WHERE Date = '2017-04-01')
SELECT
r1.KwhMeter - r2.KwhMeter, r1.OtherColumnName - r2.OtherColumnName ... FROM res1 r1, res2 r2
However ... This works perfectly on 1 row per subselect (date). Do you guarentee one entry per date? Is that a PK? You need to specify your question, mainly what do you mean by 'difference'.
I'm new to SQL and trying to create a total summary of a working SQL query. It's listing the total results from one month of data.
Now I need the total values of the outcome of the query.
So I created a 'query in a query' piece of SQL, but it ain't working because my lack of SQL knowledge. I guess it's an easy fix for you pro's :-)
The working SQL query with the daily outcome of one month:
SELECT
DATE_FORMAT(date, '%d/%m/%y') AS Datum,
COUNT(*) AS Berichten,
SUM(CASE WHEN virusinfected>0 THEN 1 ELSE 0 END) AS Virus,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND isspam>0 THEN 1 ELSE 0 END) AS Ongewenst,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND (isspam=1) AND isrblspam>0 THEN 1 ELSE 0 END) AS RBL,
SUM(size) AS Grootte
FROM
maillog
WHERE
1=1
AND (1=1)
AND
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain1.nl'
OR
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain2.nl'
GROUP BY
Datum
ORDER BY
date
The incorrect query trying to create the monthly totals:
SELECT Datum,
SUM(Berichten) AS Berichten,
SUM(Virus) AS Virus,
SUM(Ongewenst) AS Ongewenst,
SUM(RBL) AS RBL,
SUM(Grootte) AS Grootte,
FROM ( SELECT
DATE_FORMAT(date, '%d/%m/%y') AS Datum,
COUNT(*) AS Berichten,
SUM(CASE WHEN virusinfected>0 THEN 1 ELSE 0 END) AS Virus,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND isspam>0 THEN 1 ELSE 0 END) AS Ongewenst,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND (isspam=1) AND isrblspam>0 THEN 1 ELSE 0 END) AS RBL,
SUM(size) AS Grootte
FROM
maillog
WHERE
1=1
AND (1=1)
AND
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain1.nl'
OR
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain2.nl'
GROUP BY
Datum
ORDER BY
date
) t
GROUP BY Datum;
Thanks in advance.
What you want can be done with just a little addition to your first SQL statement: add with rollup after the group by clause:
GROUP BY Datum WITH ROLLUP
It will run more efficiently than the version with sub-query, although it could work that way, but you should then remove the outer group by clause and not select Datum there, since you don't want the totals per date any more, but overall.
Still, you will lose the details and only get the overall totals then. You would have to use a union with your original query to get both levels of totals. You can imagine that the with rollup modifier will do the job more efficiently.
I Have wrote sql query something like this :
SELECT `petugas_input`,
COUNT(`petugas_input`) AS `01-MAR`,
COUNT(`petugas_input`) AS `02-MAR`,
COUNT(`petugas_input`) AS `03-MAR`
FROM `tabel_arsip`
WHERE `tgl_input_arsip`>='2016-03-01 00:00:00' AND `tgl_input_arsip`<='2016-03-01 23:59:59'
GROUP BY `petugas_input`
and its generate result like this
My question is how to add criteria to the aliases column so that it will show different value on different date. (not the same value in the date column as above)
You'd have to rely on a little complex grouping:
SELECT
`petugas_input`,
SUM(CASE WHEN DATE(tgl_input_arsip) = '2016-03-01' THEN 1 ELSE 0 END) AS `01-MAR`,
SUM(CASE WHEN DATE(tgl_input_arsip) = '2016-03-02' THEN 1 ELSE 0 END) AS `02-MAR`,
SUM(CASE WHEN DATE(tgl_input_arsip) = '2016-03-03' THEN 1 ELSE 0 END) AS `03-MAR`,
FROM `tabel_arsip`
WHERE `tgl_input_arsip`>='2016-03-01 00:00:00' AND `tgl_input_arsip`<='2016-03-01 23:59:59'
GROUP BY `petugas_input`
You should not think for these hard-coded column aliases rather make a query for each petugas_input and for each date (within the given date range) along with the count.
Something like this:
SELECT
`petugas_input`,
DATE(`tgl_input_arsip`) `date`,
COUNT(*) total
FROM `tabel_arsip`
WHERE `tgl_input_arsip`>='2016-03-01 00:00:00' AND `tgl_input_arsip`<='2016-03-01 23:59:59'
GROUP BY `petugas_input`,`date`;
And you will get the following output structure:
petugas_input date total
A yyyy-mm-dd n1
B yyyy-mm-dd n2
Try this one:
SELECT `petugas_input`,
COUNT(CASE WHEN DATE(tgl_input_arsip) = '2016-03-01' THEN petugas_input ELSE 0 END) AS `01-MAR`,
COUNT(CASE WHEN DATE(tgl_input_arsip) = '2016-03-02' THEN petugas_input ELSE 0 END) AS `02-MAR`,
COUNT(CASE WHEN DATE(tgl_input_arsip) = '2016-03-03' THEN petugas_input ELSE 0 END) AS `03-MAR`
FROM `tabel_arsip`
WHERE `tgl_input_arsip`>='2016-03-01 00:00:00' AND `tgl_input_arsip`<='2016-03-03 23:59:59'
GROUP BY `petugas_input`;
:)
I have a table like this:
date day weather
2000-01-01 Monday Sunny
2000-01-02 Tuesday Rainy
. . .
I want to get number of rainy Mondays and sunny Mondays in one query like
day rainy_d sunny_d
Monday 2 5
How to accomplish that in Mysql and PostgreSQL?
select `Day`,
SUM(case when weather = 'Sunny' THEN 1 ELSE 0 end) as Sunny_D,
SUM(case when weather = 'Rainy' THEN 1 ELSE 0 end) as Rainy_D
FROM YOURTABLENAME
Where day = 'Monday'
Group by `Day`
Standard SQL, works in both:
SELECT
day,
SUM(CASE WHEN weather = 'Rainy' THEN 1 ELSE 0 END) AS rainy_d,
SUM(CASE WHEN weather = 'Sunny' THEN 1 ELSE 0 END) AS sunny_d
FROM yourtable
GROUP BY day
More concise version - MySQL only:
SELECT
day,
SUM(weather = 'Rainy') AS rainy_d,
SUM(weather = 'Sunny') AS sunny_d
FROM yourtable
GROUP BY day
More concise version - PostgreSQL only:
SELECT
day,
SUM((weather = 'Rainy')::int) AS rainy_d,
SUM((weather = 'Sunny')::int) AS sunny_d
FROM yourtable
GROUP BY day
The column day is probably redundant. I would delete it without substitution. The column date holds all the information. Then your query could look like this ..
In PostgreSQL:
SELECT to_char(date, 'Day') AS day
,COUNT(NULLIF(weather,'Sunny')) AS rainy_d
,COUNT(NULLIF(weather,'Rainy')) AS sunny_d
FROM tbl
GROUP BY 1;
In MySQL:
SELECT DAYNAME(date) AS day
... rest identical
The NULLIF() construct works for exactly two distinct (non-null) values in the column weather and is standard SQL. For more values use the alternatives provided by #Mark and #xQbert.