I have data like this
id name fromdate todate
1 abc 1993 2011
2 def 2006 2016
now i want total duration i.e how many years employee spend from 1993 to 2011 here i want to show total years spend
id name fromdate todate total_years
1 abc 1993 2011 18
2 def 2006 2016 10
query
select * from employee
Do subtract beetween todate and fromdate column:
SELECT
id,
name,
fromdate,
todate,
CASE ISNUMERIC(ISNULL(todate,'')) WHEN 0 THEN 0 ELSE CAST(todate AS INT)-CAST(fromdate AS INT) END as total_years
from employee
Your output implies that you want the following query:
select id, name, fromdate, todate,
cast(todate as int) - cast(fromdate as int) as total_years
from employee
It appears that you are storing your year columns as text. This is not a good idea, and you should use a number type instead. Actually, it would be better to store actual dates in those columns, which would make it much easier to work with.
Related
As the title states, we have test scores for students across the year, and need to see how far from the mean the current average score is each week. Each average / deviation value should be inclusive for all the data before, so we would expect that the more data we collect for a single test, the smaller the deviation becomes over time.
This part retrieves the data into a tmp table so I can play with it, getting the score for each student:
declare #tab table(testid int, studentid varchar(20), score int, wk int)
insert #tab
select testid, studentid, sum(mark), datepart(yy, collected) + datepart(isoww,
collected) from MarksTable where testid = 12345
group by testid, studentid,
datepart(yy, collected) + datepart(isoww, collected)
order by datepart(yy, collected) + datepart(isoww, collected)
So now that tmp table has testid, studentid, score and year+wk to give us a week value to group by, it looks like this:
12345 6423-96201 45 2022
12345 6423-96196 34 2022
12345 6421-96147 12 2022
12345 6423-96189 42 2023
12345 6423-96185 4 2023
(hundreds more)
I tried this:
SELECT wk,
STDEV(avg(score)) OVER (ORDER BY wk ROWS UNBOUNDED PRECEDING) AS StdDeviation FROM #tab t
GROUP BY t.wk
but that doesn't give me anything that makes sense.
e.g:
2022 NULL
2023 18.3847763108502
2031 22.1208800307161
2032 18.0739222823013
2033 18.2537667345674
2035 16.3268694692726
etc..
There's no way that can be correct.
What I need to see is the avg and deviation for each week, and these values need to include all the mark data for the weeks PRIOR to them.
Any help would be appreciated.
Stack is Microsoft SQL Server 2016 (SP1-CU15) (KB4495257)
Running this in SQL Server Management Studio v17.4
I have some sample donation history:
ID TransactionDate Amount
10 2001-12-19 00:00:00.000 75.00
10 2001-07-11 00:00:00.000 760.00
10 2010-10-15 00:00:00.000 2200.00
10 2012-08-15 00:00:00.000 1220.00
10 2013-09-16 00:00:00.000 610.00
100 2000-09-26 00:00:00.000 3000.00
100 1999-01-01 00:00:00.000 5000.00
I am trying to get a summary of giving by year by donor. The total does not have to be for consecutive years, but will just total overall # of years given. The total does not need to equal the number of transactions per year, just that the ID donated in that particular year.
For instance, ID 10 above would equal 4 indicating giving in 4 calendar years (2 donations in 2001, 1 in 2010, 1 in 2012, and 1 in 2013). ID 100 would equal 2.
The donations date back many years, so hard-coding dates is not very feasible.
Any ideas are appreciated.
Try this:
SELECT ID, DATEPART(yy,TransactionDate) Year, COUNT(*)
FROM tableName
GROUP BY ID, DATEPART(yy,TransactionDate)
You just need to return and then group on the relevant parts of your data, which is the ID and the year of the TransactionDate:
select d.ID
,count(1) as DonationYears
from(select distinct ID -- Sub query returns one row per ID and Year, which is counted up by ID in the outer query
,year(TransactionDate) as TranYear
from Donations
) d
group by d.ID
Test data:
id date company location
----------------------------------
1 03/01/2016 ABC india
2 03/25/2016 ABC us
3 02/24/2016 ABC india
4 02/25/2016 ABC us
5 03/02/2016 ABC india
Query #1
select count(id)
from table
where company = 'ABC'
and date between 03/01/2016 and 03/31/2016
Query #2
select count(id)
from table
where company = 'ABC'
and date between 02/01/2016 and 02/29/2016
Need to calculate location wise count for current and previous months.
How to write a SQL query to return location wise like the below in one query?
Expected result:
company currentmonth Previousmonth location
ABC 2 1 india
ABC 1 1 us
Try:
select company,
sum(case when month(date)=month(getdate())
then 1 else 0 end) as currentMonth,
sum(case when month(date)=month(dateadd(MONTH,-1,getdate()))
then 1 else 0 end) as Previuosmonth,
location
from yourTable
where month(date) between month(dateadd(MONTH,-1,getdate())) and month(getdate())
AND company='ABC'
group by company, location
Since you made two queries and filter both with current month (march) and previous. I decided to use the function MONTH to extract the month from the current date (getdate()) of the system and subtract 1 that way you will always have the current month and the previous one.
EDIT
As pointed out by #DhananjayaKuppu I fixed the problem for when you have a month as January, since the month function returns an integer it would return 0 in my calculation. Now it is fixed.
You may try some thing like subtract current date yearmm format (ex: 201603) and yearmm format of date from table (ex:201603) if it result to 0 treat as current month, else previous month, hope it will help:
SELECT company,
location,
sum(iif(d = 0, 0, 1)) cm,
sum(iif(d = 0, 1, 0)) pm
FROM (SELECT company,
location,
CONVERT(INT,
CONVERT(CHAR(4), GetDate(), 120) +
CONVERT(CHAR(2), GetDate(), 101)
) -
CONVERT(INT,
CONVERT(CHAR(4), date, 120) +
CONVERT(CHAR(2), date, 101)
) as d
FROM table) vw
GROUP BY company, location
I am creating a query to give number of days between two days based on year. Actually I have below type of date range
From Date: TO_DATE('01-Jun-2011','dd-MM-yyyy')
To Date: TO_DATE('31-Dec-2013','dd-MM-yyyy')
My Result should be:
Year Number of day
------------------------------
2011 XXX
2012 XXX
2013 XXX
I've tried below query
WITH all_dates AS
(SELECT start_date + LEVEL - 1 AS a_date
FROM
(SELECT TO_DATE ('21/03/2011', 'DD/MM/YYYY') AS start_date ,
TO_DATE ('25/06/2013', 'DD/MM/YYYY') AS end_date
FROM dual
)
CONNECT BY LEVEL <= end_date + 1 - start_date
)
SELECT TO_CHAR ( TRUNC (a_date, 'YEAR') , 'YYYY' ) AS YEAR,
COUNT (*) AS num_days
FROM all_dates
WHERE a_date - TRUNC (a_date, 'IW') < 7
GROUP BY TRUNC (a_date, 'YEAR')
ORDER BY TRUNC (a_date, 'YEAR') ;
I got exact output
Year Number of day
------------------------------
2011 286
2012 366
2013 176
My question is if i use connect by then query execution takes long time as i have millions of records in table and hence i don't want to use connect by clause
connect by clause is creating virtual rows against the particular record.
Any help or suggestion would be greatly appreciated.
From your vague expected results I think you want the number of records between those dates, not the number of days; but it's rather unclear. Since you refer to a table in the question I assume you want something related to the table data, not simply days between two dates which wouldn't depend on a table at all. (I have no idea what the connect by clause reference means though). This should give you that, if it is what you want:
select extract(year from date_field), count(*)
from t42
where date_field >= to_date('01-Jun-2011', 'DD-MON-YYYY')
and date_field < to_date('31-Dec-2013') + interval '1' day
group by extract(year from date_field)
order by extract(year from date_field);
The where clause is as you'd expect between two dates; I've assumed there might be times in your date field (i.e. not all at midnight) and that you want to count all records on the last date in your range. Then it's grouping and counting based on the year for each record.
SQL Fiddle.
If you want the number of days that have records within the range, then you can just vary the count slightly:
select extract(year from date_field), count(distinct trunc(date_field))
...
SQL Fiddle.
you can use the below function to reduce the number of virtual rows by considering only the years in between.You can check the SQLFIDDLE to check the performance.
First consider only the number of days between start date and the year end of that year or
End date if it is in same year
Then consider the years in between from next year of start date to the year before the end date year
Finally consider the number of days from start of end date year to end date
Hence instead of iterating for all the days between start date and end date we need to iterate only the years
WITH all_dates AS
(SELECT (TO_CHAR(START_DATE,'yyyy') + LEVEL - 1) YEARS_BETWEEN,start_date,end_date
FROM
(SELECT TO_DATE ('21/03/2011', 'DD/MM/YYYY') AS start_date ,
TO_DATE ('25/06/2013', 'DD/MM/YYYY') AS end_date
FROM dual
)
CONNECT BY LEVEL <= (TO_CHAR(end_date,'yyyy')) - (TO_CHAR(start_date,'yyyy')-1)
)
SELECT DECODE(TO_CHAR(END_DATE,'yyyy'),YEARS_BETWEEN,END_DATE
,to_date('31-12-'||years_between,'dd-mm-yyyy'))
- DECODE(TO_CHAR(START_DATE,'yyyy'),YEARS_BETWEEN,START_DATE
,to_date('01-01-'||years_between,'dd-mm-yyyy'))+1,years_between
FROM ALL_DATES;
In Oracle you can perform Addition and Substraction to dates like this...
SELECT
TO_DATE('31-Dec-2013','dd-MM-yyyy') - TO_DATE('01-Jun-2011','dd-MM-yyyy')
DAYS FROM DUAL;
it will return day difference between two dates....
select to_date(2011, 'yyyy'), to_date(2012, 'yyyy'), to_date(2013, 'yyyy')
from dual;
TO_DATE(2011,'Y TO_DATE(2012,'Y TO_DATE(2013,'Y
--------------- --------------- ---------------
01-MAY-11 01-MAY-12 01-MAY-13
select to_char(date_field,'yyyy'), count(*)
from your_table
where date_field between to_date('01-Jun-2011', 'DD-MON-YYYY')
and to_date('31-Dec-2013 23:59:59', 'DD-MON-YYYY hh24:mi:ss')
group by to_char(date_field,'yyyy')
order by to_char(date_field,'yyyy');
Hy. There are employee records in my postgresql database something like
CODE DATE COUNT
"3443" "2009-04-02" 3
"3444" "2009-04-06" 1
"3443" "2009-04-06" 1
"3443" "2009-04-07" 7
I want to use a query "SELECT ALL CODES AND COUNT THEM THAT OCCURRED IN THE MONTH"
RESULT:
CODE DATE COUNT
"3443" "2009-04" 3
"3441" "2009-04" 13
"3442" "2009-04" 11
"3445" "2009-04" 72
I did use a query i.e.
SELECT CODE,date_part('month',DATE),count(CODE)
FROM employee
where
group by CODE,DATE
The above query runs fine but the months listed in the records are in form of numbers and its hard to find that a month belongs to which year. In short I want to get the result just like mention above in the RESULT section. Thanks
Try this:
SELECT CODE, to_char(DATE, 'YYYY-MM'), count(CODE)
FROM employee
where
group by CODE, to_char(DATE, 'YYYY-MM')
Depending on whether you want the result as text or a date, you can also write it like this:
SELECT CODE, date_trunc('month', DATE), COUNT(*)
FROM employee
GROUP BY CODE, date_trunc('month', DATE);
Which in your example would return this, with DATE still a timestamp, which can be useful if you are going to do further calculations on it since no conversions are necessary:
CODE DATE COUNT
"3443" "2009-04-01" 3
"3441" "2009-04-01" 13
"3442" "2009-04-01" 11
"3445" "2009-04-01" 72
date_trunc() also accepts other values, for instance quarter, year etc.
See the documentation for all values
Try any of
SELECT CODE,count(CODE),
DATE as date_normal,
date_part('year', DATE) as year,
date_part('month', DATE) as month,
to_timestamp(
date_part('year', DATE)::text
|| date_part('month', DATE)::text, 'YYYYMM')
as date_month
FROM employee
where
group by CODE,DATE;