Sum of column between any two dates? - sql

Say I have a table with 3 columns, Name, Date, Test Score.
I want to find the sum of a person’s test scores between any two dates.
How would I do this in SQL?
Obviously I’d need the dates and the person (Name) as parameters, but I’m not sure where to go from here?

You would typically use the parameters in the where clause of an aggregate query:
select sum(test_score) total_score
from mytable
where
date >= :start_date
and date < :end_date
and name = :name
:start_date, :end_date and :name are the parameters to the query, which are used to filter the dataset; the query always returns one row, with a single column called total_score that contains the sum of test_score for rows that satisfy the filtering predicataes. If no row matches, the returned value is null.

With the following table:
CREATE TABLE `marks` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255),
`date` DATETIME,
`score` INT
);
The following select SQL will display a list of names and their sum:
SELECT
`name`,
SUM(`score`) AS `score_sum`
FROM `marks`
WHERE `date`
BETWEEN '2020-06-01 00:00:00'
AND '2020-06-22 13:14:25'
GROUP BY `name`;
But for your solution, you might want to use:
SELECT
SUM(`score`) AS `score_sum`
FROM `marks`
WHERE
`name` = 'xxx'
AND `date`
BETWEEN '2020-06-01 00:00:00'
AND '2020-06-22 13:14:25';
Just keep in mind that if there is no value to SUM, a null will be returned.

Related

Group by count multiple tables

Need to find out why my group by count query is not working. I am using Microsoft SQL Server and there are 2 tables I am trying to join.
My query needs to bring up the number of transactions made for each type of vehicle. The output of the query needs to have a separate row for each type of vehicle such as ute, hatch, sedan, etc.
CREATE TABLE vehicle
(
vid INT PRIMARY KEY,
type VARCHAR(30) NOT NULL,
year SMALLINT NOT NULL,
price DECIMAL(10, 2) NOT NULL,
);
INSERT INTO vehicle
VALUES (1, 'Sedan', 2020, 240)
CREATE TABLE purchase
(
pid INT PRIMARY KEY,
vid INT REFERENCES vehicle(vid),
pdate DATE NOT NULL,
datepickup DATE NOT NULL,
datereturn DATE NOT NULL,
);
INSERT INTO purchase
VALUES (1, 1, '2020-07-12', '2020-08-21', '2020-08-23')
I have about 10 rows on information in each table I just haven't written it out.
This is what I wrote but it doesn't return the correct number of transactions for each type of car.
SELECT
vehicle.vid,
COUNT(purchase.pid) AS NumberOfTransactions
FROM
purchase
JOIN
vehicle ON vehicle.vid = purchase.pid
GROUP BY
vehicle.type;
Any help would be appreciated. Thanks.
Your GROUP BY and SELECT columns are inconsistent. You should write the query like this:
SELECT v.Type, COUNT(*) AS NumPurchases
FROM Purchase p JOIN
Vehicle v
ON v.vID = p.pID
GROUP BY v.Type;
Note the use of table aliases so the query is easier to write and read.
If this doesn't produce the expected values, you will need to provide sample data and desired results to make it clear what the data really looks like and what you expect.

Postgres union query returns strange result

I have the following tables:
CREATE TABLE geodat(
vessel UUID NOT NULL,
trip UUID NOT NULL,
geom geometry(LineString,4326),
PRIMARY KEY(vessel,trip)
);
CREATE TABLE areas(
gid SERIAL NOT NULL,
/* --other columns of little interest-- */
geom geometry(MultiPolygon,3035),
PRIMARY KEY(gid)
);
The following query is supposed to return the area that has been crossed the least, as well as how many times it was crossed and by which vessels.
SELECT vessel,MIN(cnt) as min_crossing,gid
FROM (
SELECT vessel,COUNT(*) as cnt, gid
FROM (
SELECT vessel, null as geo1, geom as geo2, null as gid
FROM geodat
UNION ALL
SELECT null,geom,null,gid FROM areas ) as P
WHERE ST_Crosses(geo1,geo2) AND geo1 IS NOT NULL AND geo2 IS NOT NULL
GROUP BY gid,vessel) as P1
GROUP BY gid,vessel
Theoretically, this query should solve the question above. The problem is that I am getting (0 rows) as an answer, although I have been assured as to the opposite. I discovered it has something to do with the null values the UNION produced, but I don't have a clue how to fix this.
Any ideas?
NOTE: The two tables have 31822 rows and 27308 rows respectively which makes a JOIN impractical.
You have the condition
WHERE ST_Crosses(geo1,geo2) AND geo1 IS NOT NULL AND geo2 IS NOT NULL
However, in the union all you are explicitly setting geo1 to null and geo2 to null. Hence the query is returning 0 rows.
You can change the where condition above to or, which would return rows.
WHERE ST_Crosses(geo1,geo2) AND geo1 IS NOT NULL OR geo2 IS NOT NULL

Can't aggregate on max function

I have a table
CREATE TABLE `messages` ( `uid` BIGINT NOT NULL ,
`mid` BIGINT , `date` BIGINT NOT NULL , PRIMARY KEY (`mid`));
I want to select max(date) grouped by uid, i.e. for every uid(read user) I want to find the latest message (with tha maximum date)
tried this
select messages.mid, max(messages.date), messages.uid, messages.body
from messages
where messages.chat_id is NULL
group by messages.uid
but the query works wrong.
A subquery can give you the date you need in order to retrieve the newest message for each user:
SELECT messages.uid, messages.mid, messages.date, messages.body
FROM messages
WHERE messages.chat_id IS NULL
AND messages.date IN
( SELECT MAX(m2.date) FROM messages m2
WHERE m2.uid = messages.uid AND m2.chat_id IS NULL
)
;
u need to group by all the fields while using aggregate functions :) using a subquery would sort out the problem.
SELECT messages.date,messages.uid, messages.mid, messages.body
FROM messages
WHERE messages.chat_id IS NULL AND messages.date IN (SELECT MAX(msg.date) FROM messages msg WHERE messages.chat_id IS NULL And msg.uid = messages.uid )
alternatively it can also be done using the 'having' clause
done :)

SQL query to get records even if count is 0 in one table

select id, name, 'First Category' as category, count(id) as totalCalls
from missed_call
where name = 'whatever1'
group by name, category
UNION
select id, name, 'Second Category' as category, count(id) as totalCalls
from missed_call
where name = 'whatever2'
group by name, category
order by name ASC, totalCalls DESC
The previous query will not retrieve the records where totalCalls is 0.
So, how can I do to get those records and present totalCalls as 0?
UPDATE: I have tried changing count(id) as totalCalls for IFNULL(count(id), 0) as totalCalls but it doesn't solve the problem. Perhaps, because count(id) is actually not null, it just does not exist.
If you are unwilling to expand your database schema you can always pretend there is a table:
select surrogateTable.name,
surrogateTable.Category,
count(id) as totalCalls
from
(
select 'whatever1' Name,
'First Category' Category
union all
select 'whatever2',
'Second Category'
) surrogateTable
left join missed_call
on surrogateTable.Name = missed_call.Name
group by surrogateTable.name, surrogateTable.category
I dropped id in select because you should not select something you are not grouping on - this is probably MySql.
Check this on Sql Fiddle.
Your problem is that you only look at missed calls and not at categories, so you cannot notice categories that have no corresponding missed calls.
Here is the skeleton that will do that, supposing you will adapt it to the real structure of the category table.
SELECT ...
FROM Category cat
LEFT JOIN missed_call call ON call.category = category.id
WHERE (call.name = 'whatever1' OR call.category IS NULL)
GROUP BY call.name, call.category
...
Note especially call.category IS NULL. The column is supposedly not nullable; so this really checks for a Category row without any corresponding calls, an artifact of the outer join.
You should define a table named category to contain a complete list of all category names that are possible, even those that have no calls assigned to them (i.e. zero).
create table category
(
id numeric(10,0) NOT NULL,
name varchar(10) NULL
)
Then you can query the full list of categories from this table and LEFT JOIN the results against what you have from above.
You can then amend your missed_call to use foreign keys against your new category table for better efficiency and better schema design
create table missed_call
(
id numeric(10,0) NOT NULL,
first_category_id numeric(10,0) NULL,
second_category_id numeric(10,0) NULL,
name varchar(12)
)

How can I join these tables?

I have a number of devices logging different data at different times and want to get all the data in a single query, ordered by time. An example of the kinds of tables I have:
CREATE TABLE location(
device_id INT, -- REFERENCES device(id)
timestamp DATETIME2 NOT NULL,
position GEOMETRY NOT NULL
)
CREATE TABLE temperature(
device_id INT, -- REFERENCES device(id)
timestamp DATETIME2 NOT NULL,
temp FLOAT NOT NULL
)
I want to have a single query that joins the tables on device_id and timestamp that contains nulls when the timestamps don't match. An example of the output format I am seeking is:
device_id, timestamp, location, temperature
1, 2011/12/1 10:00:00, (35.1, 139.2), NULL
1, 2011/12/1 10:00:01, NULL, 9.0
1, 2011/12/1 10:00:02, (35.1, 139.2), 9.1
I've tried doing FULL JOIN but cannot figure out how to do the timestamp column without a huge CASE statement (keep in mind although I've only shown 2 tables, this can have many more).
SELECT
location.device_id,
CASE WHEN location.timestamp IS NOT NULL THEN
location.timestamp
ELSE
temperature.timestamp
END as timestamp,
location,
temp
FROM
location
FULL JOIN temperature ON location.device_id = temperature.device_id
AND location.timestamp = temperature.timestamp
ORDER BY
timestamp
Is there a simpler way to write this kind of query?
You can use the COALESCE expression.
SELECT
location.device_id,
COALESCE(location.timestamp, temperature.timestamp) as timestamp,
position,
temp
FROM
location
FULL JOIN temperature ON location.device_id = temperature.device_id
AND location.timestamp = temperature.timestamp
ORDER BY
timestamp;
Yes, you can use an OUTER Join to the temperature table. That will return nulls in the case where there is no matching row in the temperature table.
You need a COALESCE to get the device_id/timestamp, as follows:
SELECT
COALESCE(l.device_id, t.device_id) as device_id,
COALESCE(l.timestamp, t.timestamp) as timestamp,
l.position as location,
t.temp as temperature
FROM location l
FULL JOIN temperature t ON l.device_id = t.device_id
AND l.timestamp = t.timestamp
ORDER BY 2
Also note the increased readability by aliasing the tables with very short names (l and t).
You may want to review your ordering - perhaps you want ORDER BY 1, 2 instead
SELECT device_id, timestamp, position, NULL AS temp
FROM location
UNION ALL
SELECT device_id, timestamp, NULL AS position, temp
FROM temperature
ORDER
BY timestamp;
Note the ALL keyword is required here.