sql server sum column to find out who reached a value first - sql-server

I asked a question similar to this here:
sql sum a column and also return the last time stamp
It turns out my use case was incorrect though so I need to make an adjustment to my question.
I've got a table in SQL Server with several columns. The relevant ones are:
name
distance
create_date
I have many people identified by name, and every few days they travel a certain distance. For example:
name distance create_date
john 15 09/12/2014
john 20 09/22/2014
alex 10 08/15/2014
alex 12 09/05/2014
alex 20 09/12/2014
john 8 09/30/2014
alex 30 09/14/2014
mike 12 09/10/2014
The query I need has 3 parameters:
#start_date
#end_date
#count
I need a query that between the two dates, returns for each person the distance traveled. The trick though is that for each person I should sum to an amount just past the amount indicated in #count and return the date this was achieved, or if the person did not pass the #count then return the sum and last date of entry. So for example, if I use the parameters:
#start_date=08/01/2014
#end_date=09/25/2014
#count=35
I would expect the following:
name distance create_date
alex 42 09/12/2014
john 35 09/22/2014
mike 12 09/10/2014
Does someone have an idea for this?
Thank you!

As per my understanding the query for the the person who crossed particular count
select A.name,sum(A.distance),A.create_date from (select name,distance,create_date
from table where create_date between #start_date and #end_date)A group by A.name,A.create_date
having sum(A.distance)>#count
Those who doesnt cross
select A.name,sum(A.distance),max(A.create_date) from (select name,distance,create_date
from table where create_date between #start_date and #end_date)A group by A.name
having sum(A.distance)<#count

Related

Query for date groupings?

I have a Client table with basic demographics in rows and a date of birth row (DOB) with a DATE data type.
I'm trying to make a query that will take my entries and count how many clients are between the ages of 18-60, 61-79, 80+. I'm not sure if I'm having a brain-fart, but I can't figure out how to gather that info from my table...
So what I have is:
Last Name First Name DOB
Stein Ethel 1954-01-20
Frank Sam 1981-05-65
etc...
What I want to have is:
Ages 18-60
6
Ages 61-79
10
Ages 80+
20
Any recommendations to proceed?
Using #Alex's suggestion
declare #today datetime
set #today =getdate()
select
s as [start],
e as [end],
count(1) as [count]
from Client join
(values (0,17),(18,60),(61,79),(80,9999)) as ranges(s,e)
on datediff(yy,dob,#today) between s and e
-- where buildingid=1
group by s,e
See demo here

SQL Optimize Group By Query

I have a table here with following fields:
Id, Name, kind. date
Data:
id name kind date
1 Thomas 1 2015-01-01
2 Thomas 1 2015-01-01
3 Thomas 2 2014-01-01
4 Kevin 2 2014-01-01
5 Kevin 2 2014-01-01
5 Kevin 2 2014-01-01
5 Kevin 2 2014-01-01
6 Sasha 1 2014-01-01
I have an SQL statement like this:
Select name,kind,Count(*) AS RecordCount
from mytable
group by kind, name
I want to know how many records there are for any name and kind. Expected results:
name kind count
Thomas 1 2
Thomas 2 1
Kevin 2 2
Sasha 1 4
The problem is that it is a big table, with more than 50 Million records.
Also I'd like to know the result within the last hour, last day, last week and so on, for which I need to add this WHERE clause this:
Select name,kind,Count(*) AS RecordCount
from mytable
WHERE Date > '2015-26-07'
group by kind, name
I use T-SQL with the SQL Server Management Studio. All of the relevant columns have a non clustered index and the primary key is a clustered index.
Does somebody have ideas how to make this faster?
Update:
The execution plan says:
Select, Compute Scalar, Stream Aggregate, Sort, Parallelism: 0% costs.
Hash Match (Partial Aggregate): 12%.
Clustered Index Scan: 88%
Sorry, I forgot to check the SQL-statements.
50 million is just lot of rows
Not anything you can do to optimize that query that I can see
Possibly a composite index on kind, name
Or try name, kind
Or name only
I think the query optimizer is smart enough for this to not be a factor but but switch the group by to name, kind as name is more unique
If kind is not very unique (just 1 and 2) then you may be better off no index on that
I defrag the indexes you have
To query the last day is no big deal because you already have a date column on witch you can put an index on.
For last week I would create a seperate date-table witch contains one row per day with columns id, date, week
You have to pre-calculate the week. And now if you want to query a specific week you can look in the date table, get the Dates and query only those dates from your tabele mytable
You should test if it is more performant to join the date columns or if you better put the id column in your myTable an join with id. For big tables id might be the better choice.
To query last hour you could add the column [hour] in myTable an query it in combination with the date

Crystal Report do not do the sum on Database "Cache"

I am using Crystal report to do a sum over 3 columns. The table structure looks like:
table #test (Country VARCHAR(10), Name VARCHAR(10), Weight VARCHAR(10), Qty INT)
I wrote a query in the crystal command pane when I do the connection:
SELECT Country, SUM(Qty) As Qty, Name, Weight FROM #test GROUP BY Country, Name, Weight
I should get something like:
CANADA 2 John 200
US 1 John 160
US 2 Mike 180
US 6 Sam 90
However, the crystal report does not sum the field, instead it pulls every single row, and the result looks like I write the query:
SELECT Country, Qty, Name, Weight FROM #test
CANADA 1 John 200
CANADA 1 John 200
US 1 John 160
US 2 Mike 180
US 3 Sam 90
US 3 Sam 90
By the way, the backend database is called "Cache". It might be due to there are some hidden characters, but I cannot see them. I have used replace (char(10)), replace (char(13) and trim to try to clean.
I also try to pull the table column directly without writing the query, but I do not know how to sum three columns (Country, Name and Weight). I only know how to sum one column. By the way, the request do not want the details, only the sum over these three columns;
First group by country.
Create one more group by quantity
Create one more group by name
Place weight in details and take the sum for all 3 groups if you need or only particular group
Supress tje details.

T-SQL - Getting most recent date and most recent future date

Assume the table of records below
ID Name AppointmentDate
-- -------- ---------------
1 Bob 1/1/2010
1 Bob 5/1/2010
2 Henry 5/1/2010
2 Henry 8/1/2011
3 John 8/1/2011
3 John 12/1/2011
I want to retrieve the most recent appointment date by person. So I need a query that will give the following result set.
1 Bob 5/1/2010 (5/1/2010 is most recent)
2 Henry 8/1/2011 (8/1/2011 is most recent)
3 John 8/1/2011 (has 2 future dates but 8/1/2011 is most recent)
Thanks!
Assuming that where you say "most recent" you mean "closest", as in "stored date is the fewest days away from the current date and we don't care if it's before or after the current date", then this should do it (trivial debugging might be required):
SELECT ID, Name, AppointmentDate
from (select
ID
,Name
,AppointmentDate
,row_number() over (partition by ID order by abs(datediff(dd, AppointmentDate, getdate()))) Ranking
from MyTable) xx
where Ranking = 1
This usese the row_number() function from SQL 2005 and up. The subquery "orders" the data as per the specifications, and the main query picks the best fit.
Note also that:
The search is based on the current date
We're only calculating difference in days, time (hours, minutes, etc.) is ignored
If two days are equidistant (say, 2 before and 2 after), we pick one randomly
All of which could be adjusted based on your final requirements.
(Phillip beat me to the punch, and windowing functions are an excellent choice. Here's an alternative approach:)
Assuming I correctly understand your requirement as getting the date closest to the present date, whether in the past or future, consider this query:
SELECT t.Name, t.AppointmentDate
FROM
(
SELECT Name, AppointmentDate, ABS(DATEDIFF(d, GETDATE(), AppointmentDate)) AS Distance
FROM Table
) t
JOIN
(
SELECT Name, MIN(ABS(DATEDIFF(d, GETDATE(), AppointmentDate))) AS MinDistance
FROM Table
GROUP BY Name
) d ON t.Name = d.Name AND t.Distance = d.MinDistance

Microsoft T-SQL Counting Consecutive Records

Problem:
From the most current day per person, count the number of consecutive days that each person has received 0 points for being good.
Sample data to work from :
Date Name Points
2010-05-07 Jane 0
2010-05-06 Jane 1
2010-05-07 John 0
2010-05-06 John 0
2010-05-05 John 0
2010-05-04 John 0
2010-05-03 John 1
2010-05-02 John 1
2010-05-01 John 0
Expected answer:
Jane was bad on 5/7 but good the day before that. So Jane was only bad 1 day in a row most recently. John was bad on 5/7, again on 5/6, 5/5 and 5/4. He was good on 5/3. So John was bad the last 4 days in a row.
Code to create sample data:
IF OBJECT_ID('tempdb..#z') IS NOT NULL BEGIN DROP TABLE #z END
select getdate() as Date,'John' as Name,0 as Points into #z
insert into #z values(getdate()-1,'John',0)
insert into #z values(getdate()-2,'John',0)
insert into #z values(getdate()-3,'John',0)
insert into #z values(getdate()-4,'John',1)
insert into #z values(getdate(),'Jane',0)
insert into #z values(getdate()-1,'Jane',1)
select * from #z order by name,date desc
Firstly, I am sorry but new to this system and having trouble figuring out how to work the interface and post properly.
2010-05-13 ---------------------------------------------------------------------------
Joel, Thank you so much for your response below! I need it for a key production job that was running about 60 minutes.
Now the job runs in 2 minutes!!
Yes, there was 1 condition in my case that I needed to address. My source always had only record for those that had
been bad recently so that was not a problem for me. I did however have to handle records where they were never good,
and did that with a left join to add back in the records and gave them a date so the counting would work for all.
Thanks again for your help. It was opened my mind some more to SET based logic and how to approach it and was
a HUGE benefit to my production job.
The basic solution here is to first build a set that contains the name of each person and the value of the last day on which that person was good. Then join this set to the original table and group by name to find the count of days > the last good day for each person. You can build the set in either a CTE, a view, or an uncorrelated derived table (sub query) — any of those will work. My example below uses a CTE.
Note that while the concept is sound, this specific example might not return exactly what you want. Your actual needs here depend on what you want to happen for those who have not been good ever and for those who have not been bad recently (ie, you might need a left join to show users who were good yesterday). But this should get you started:
WITH LastGoodDays AS
(
SELECT MAX([date]) as [date], name
FROM [table]
WHERE Points > 0
GROUP BY name
)
SELECT t.name, count(*) As ConsecutiveBadDays
FROM [table] t
INNER JOIN LastGoodDays lgd ON lgd.name = t.name AND t.[date] > lgd.[date]
group by t.name

Resources