Grouping with Nulls T-SQL - sql-server-2008

I have this problem with a SQL query (SQL Server 2008)
SELECT id, client, SUM(debt), date
FROM Table GROUP BY id, client, date
Returned from the query is
id client debt date
1 jim x 500 05/05/2012
2 jack a 900 06/06/2012
2 jack a 500 null
Is there a way to add in this scenario Jack a's debt (1400) and display the non null date i.e. 06/06/2012.
A person can only have 2 records max and 1 record is always date null so is there a way to do the sum and use the date that is not null?
Thanks

To group by client you have to remove id and date from your GROUP BY:
SELECT
MAX(id) AS newest_id, -- or MIN(id) if you prefer
client,
SUM(debt) AS total_debt,
MAX(date) AS most_recent -- or MIN(date) if you prefer
FROM YourTable
GROUP BY client

Related

get the most recent two dates for a customer MySQL

I need to retrieve the last two dates for customers with entries in at least two different dates, implying there are some customer who had purchased only in one date, the table is as follow
client_id date
1 2016-07-02
1 2016-07-02
1 2016-06-01
2 2015-06-01
as a response, I would get
client_id previous_date last_date
1 2016-06-01 2016-07-02
important:
a client can have multiple entries for the same date
a client can have entries only for one date, such customer should be discarded
Try this: group by the client_id column, with a having of count(*) > 1 to find results with more than one result. Then do a check of the min and max date, to ensure they aren't the same. Then just select the date, and order the results by date in desc order, with a limit of 2.
select
date
from
my_table
group by
client_id
having
min(date) <> max(date)
and count(*) > 1
order by
date desc
limit 2

Grouping all in one tuple in SQL

I have a table EMP with employees id and their hireyear. And I have to get the amount of hired employees in lets say the the years 2002 and 2000. The output table should als contain the amount of hired employees in the whole time.
So the last is easy. I just have to write:
SELECT COUNT(id) AS GLOBELAMOUNT FROM EMP;
But how do I count the amount of hired employees in 2002?
I could write the following:
SELECT COUNT(id) AS HIREDIN2002 FROM EMP WHERE YEAR = 2002;
But how do I combine this in one tuple with the data above?
Maybe I should group the data by Hireyear first and then count it? But can not really imagine how I count the data for several years.
Hope u guys can help me.
Cheers,
Andrej
Use conditional aggregation, e.g.:
SELECT COUNT(id) AS GLOBELAMOUNT,
COUNT(CASE WHEN YEAR=2000 THEN 1 END) AS HIREDIN2000,
COUNT(CASE WHEN YEAR=2002 THEN 1 END) AS HIREDIN2002
FROM EMP;
In Microsoft SQL Server (Transact-SQL) at least, you can use a windowed aggregate function like this:
Select Distinct
Year
,count(Id) over (Partition by Year) as CountHiredInYear
,count(Id) over () as CountTotalHires
From EMP
This gives something like:
Year | CountHiredInYear | CountTotalHires
2005 | 3 | 12
2006 | 4 | 12
2007 | 5 | 12
Another SQL Server specific approach is the With Rollup keyword.
Select Year
,count(Id) as CountHires
From Emp
Group by Year
With Rollup
This adds a summary line for each level of grouping, with the total value for that set of rows. So here, you'd get an extra row where Year was NULL, with the value 12.
You could use two (or more) inline queries:
SELECT
(SELECT COUNT(id) FROM EMP) AS GLOBELAMOUNT,
(SELECT COUNT(id) FROM EMP WHERE YEAR = 2002) AS HIREDIN2002
or a CROSS JOIN:
SELECT GLOBELAMOUNT, HIREDIN2002
FROM
(SELECT COUNT(id) AS GLOBELAMOUNT FFROM EMP) g CROSS JOIN
(SELECT COUNT(id) AS HIREDIN2002 FROM EMP WHERE YEAR = 2002) h

How can I write a query that aggregate a single row with latest date among multiple set of rows?

I have a MySQL table where there are many rows for each person, and I want to write a query which aggregates rows with special constraint. (one per person)
For example, lets say the table is consist of following data.
name date reason
---------------------------------------
John 2013-04-01 14:00:00 Vacation
John 2013-03-31 18:00:00 Sick
Ted 2012-05-06 20:00:00 Sick
Ted 2012-02-20 01:00:00 Vacation
John 2011-12-21 00:00:00 Sick
Bob 2011-04-02 20:00:00 Sick
I want to see the distribution of 'reason' column. If I just write a query like below
select reason, count(*) as count from table group by reason
then I will be able to see number of reasons for this table overall.
reason count
------------------
Sick 4
Vacation 2
However, I am only interested in single reason from each person. The reason that should be counted should be from a row with latest date from the person's records. For example, John's latest reason would be Vacation while Ted's latest reason would be Sick. And Bob's latest reason (and the only reason) is Sick.
The expected result for that query should be like below. (Sum of count will be 3 because there are only 3 people)
reason count
-----------------
Sick 2
Vacation 1
Is it possible to write a query such that single latest reason will be counted when I want to see distribution(count) of reasons?
Here are some facts about the table.
The table has tens of millions of rows
For most of times, each person has one reason.
Some people have multiple reasons, but 99.99% of people have fewer than 5 reasons.
There are about 30 different reasons while there are millions of distinct names.
The table is partitioned based on date range.
SELECT T.REASON, COUNT(*)
FROM
(
SELECT PERSON, MAX(DATE) AS MAX_DATE
FROM TABLE-NAME
GROUP BY PERSON
) A, TABLE-NAME T
WHERE T.PERSON = A.PERSON AND T.DATE = A.MAX_DATE
GROUP BY T.REASON
Try this
select reason, count(*) from
(select reason from table where date in
(select max(date) from table group by name)) t
group by reason
In MySQL, it's not very efficient to do this kind of query since you don't have access to tools like partitionning query in SQL Server or Oracle.
You can still emulate it by doing a subquery and retrieve the rows based on the condition you need, here the maximum date :
SELECT t.reason, COUNT(1)
FROM
(
SELECT name, MAX(adate) AS maxDate
FROM #aTable
GROUP BY name
) maxDateRows
INNER JOIN #aTable t ON maxDateRows.name = t.name
AND maxDateRows.maxDate = t.adate
GROUP BY t.reason
You can see a sample here.
Test this query on your samples, but I'm afraid that it will be slow as hell.
For your information, you can do the same thing in a more elegant and much much faster way in SQL Server :
SELECT reason, COUNT(1)
FROM
(
SELECT name
, reason
, RANK() OVER(PARTITION BY name ORDER BY adate DESC) as Rank
FROM #aTable
) AS rankTable
WHERE Rank = 1
GROUP BY reason
The sample is here
If you are really stuck to MySql, and the first query is too slow, then you can split the problem.
Do a first query creating a table:
CREATE TABLE maxDateRows AS
SELECT name, MAX(adate) AS maxDate
FROM #aTable
GROUP BY name
Then create index on both name and maxDate.
Finally, get the results :
SELECT t.reason, COUNT(1)
FROM maxDateRows m
INNER JOIN #aTable t ON m.name = t.name
AND m.maxDate = t.adate
GROUP BY t.reason
The solution you are looking for seems to be solved by this query :
select
reason,
count(*)
from (select * from tablename group by name) abc
group by
reason
It is quite fast and simple. You can view the SQL Fiddle
Apologies if this answer duplicates an existing. Maybe I'm suffering from some form aphasia but I cannot see it...
SELECT x.reason
, COUNT(*)
FROM absentism x
JOIN
( SELECT name,MAX(date) max_date FROM absentism GROUP BY name) y
ON y.name = x.name
AND y.max_date = x.date
GROUP
BY reason;

SQL Running Total Grouped by Date

(Using SQL Server 2008) I could easily get this to work if I built a view, and then a query on the view, but I want to perform this in one SQL query. I have a table that contains two columns (DeliveredDate (DateTime), Delivered (Varchar)). I am first converting DeliveredDate to Date only, and then grouping by Date. At the same time I am performing a Count on Delivered (column is either YES or NULL). Here is what I use to accomplish this portion:
SELECT CAST([DeliveredDate] As Date),
COUNT([Delivered])
FROM [TableName]
GROUP BY CAST([DeliveredDate] As Date)
ORDER BY CAST([DeliveredDate] As Date)
As an output, I get something like:
DeliveredDate | Delivered
2012-04-24 10
2012-04-25 500
2012-04-26 422
2012-04-27 33
What I'm looking for is something like this:
DeliveredDate | Delivered | RunningTotal
2012-04-24 10 10
2012-04-25 500 510
2012-04-26 422 932
2012-04-27 33 965
I've tried various examples I've seen out there, but none seem to match this scenario of performing a Count and a RunningTotal on said Count.
If you are using a product that implements ordered OVER clauses for SUM, you can do something like this:
select
cast(DeliveredDate as date) as DeliveredDate,
count(Delivered) as Delivered,
sum(count(Delivered)) over (
order by DeliveredDate
) as RunningTotal
from Orders
group by OrderDate
order by OrderDate;
Your expression count(Delivered) is a bit strange. Just to be sure it's what you want: it will count the number of rows on the particular date for which the value in the Delivered column is NOT NULL.
I'm not sure if a CTE counts as a view but this will work in SQL 2005+ which Does Not Support ordered OVER clauses for SUM,
WITH cte (DeliveredDate, Delivered)
AS (SELECT Cast([DeliveredDate] AS DATE) DeliveredDate,
Count([Delivered]) Delivered
FROM [TableName]
GROUP BY Cast([DeliveredDate] AS DATE))
SELECT d1.delivereddate,
d1.delivered,
Sum(d2.Delivered) RunningTotal
FROM cte d1
LEFT JOIN cte d2
ON d1.delivereddate >= d2.DeliveredDate
GROUP BY d1.delivereddate,
d1.delivered
ORDER BY d1.delivereddate

MS Access 2003 - Combine last record of multiple tables into one query or table?

I have a couple of tables that are transaction tables, and I would like to make a simple pivot chart for comparative balances....which happen to be the last record of each of these tables in a field called "balance".
so i know how to populate this on a form using a SQL statement, rs.movelast, but i do not know how to get to the pivot chart without having this into a table....
thanks!
EDIT:
This is what I used! Thanks Remou!
(SELECT TOP 1 TransactionID, Balance
FROM tblTrans001
ORDER BY TransctionID DESC)
UNION
(SELECT TOP 1 TransactionID, Balance
FROM tblTransaction02
ORDER BY TransactionID DESC)
UNION
(SELECT TOP 1 TransactionID, Balance
FROM Tranaction03
ORDER BY TransID DESC)
Now I just need to find a way to insert a text string into the corresponding fields that identifies what table the value is coming from.
for example, the above query returns
TransID Balance
123 $1000.00
234 $20000.00
345 $300000.00
and I need:
TransID Balance Table/Account
123 $1000.00 tblTransaction01
234 $20000.00 tblTransaction02
345 $300000.00 tblTransaction03
thanks!
What do you define last record? Let us say it is the date created and that the date created is unique, then you could use the SQL below. Note that the parentheses are important.
(SELECT TOP 1 CrDate , Balance , "TranA" As FromTable
FROM TransactionsA
ORDER BY CrDate DESC)
UNION
(SELECT TOP 1 CrDate , Balance , "TranB" As FromTable
FROM TransactionsB
ORDER BY CrDate DESC)