Date is wrong in SQL Server - sql-server

I have written a HQL query, which creates a statistic report, with daily counts.
select cast(time as date), count(*) from table
group by cast(time as date)
The Eclipse Hibernate plugin reports a dynamic SQL preview, which is when run, works properly. The generated query looks like this:
select cast(time as date) as col_0_0_, count(*) as col_1_0_
from TABLE table0_ group by cast(time as date)
Everything is fine up until now, however when I run the HQL query, the date I get back is wrong (it is offset by exactly two days). This really looks like the same issue as in this question, however the hotfix does not solve it. I am using the latest Microsoft SQL Server driver, JDK 7 and Hibernate 4.3.8.
Have anyone encountered this issue?
These are my actual results:
Received output
Date | Count
--------------+---------
2015-03-01 | 1
2015-03-02 | 43
2015-03-03 | 29
Expected output
Date | Count
--------------+---------
2015-03-03 | 1
2015-03-04 | 43
2015-03-05 | 29
I have also noted, that when I cast the date to a string, it works properly, so
cast(cast(time as date) as text)
yields correct results.

Related

What is the suggested date format to use in SQL Server?

I've seen a few variations of writing dates in SQL Server as it doesn't support the more standard literal format of:
DATE '2014-01-01'
Is there a suggested way to write date-literals (or the closest thing to it) in SQL Server? Currently what I do is:
CAST('2014-01-01' AS date)
Whenever I want to use a date. Is this the most common?
SQL Server supports some date formats, but you can use '20220101'
CREATE TABLE t1([date] date)
INSERT INTO t1 values ('20220101')
SELECT * FROM t1
| date |
| :--------- |
| 2022-01-01 |
db<>fiddle here
You are misunderstanding SQL for TSQL.
In your SELECT date, '20220101' FROM t1the second is a string for SQL.
But as you see in the query below, TSQL will converts the text into a date automatically when comparing for example
SELECT CASE WHEN CAST('2014-01-01' AS date) > '20220101' THEN 'TRUE' ELSE 'FALSE' END
| (No column name) |
| :--------------- |
| FaLSE |
db<>fiddle here

SQL Server 2008 - False error for "Msg 8120"?

I am writing a query in SQL Server 2008 (Express I believe?). I am currently getting this error:
Msg 8120, Level 16, State 1, Line 16
Column 'AIM.dbo.AggTicket.TotDirectHrs' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I am trying to do a historical analysis of our production WIP (Work In Process).
I have created a standalone calendar table (actually located in a separate database called BAS on the same server to not interfere with the ERP that operates the AIM database). I've been overwhelmed for days with some of the examples for creating running total queries/views/tables, so for now I'll just plan on taking care of that part inside of Crystal Reports 2016. My thinking was that I wanted to return records for each order each day of my calendar table (to be narrowed down in the future to only days that match records in the AIM database). The values I think I will need are:
Record Date (not unique)
Order Number (unique for each day)
Estimated hours for the job
The total number of hours worked on the job current as of today's date (in case the estimated hours were drastically underbudgeted)
The SUM of the direct labor hours charged to the job on said record date
The COUNT of the number of employees in attendance on said record date.
The SUM of the hours attended by employees on said record date.
The tables I use are as follows:
BAS Database:
dbo.DateDimension - Used for complete calendar of dates from 1/1/1987 to 12/31/2036
AIM Database:
dbo.AggAttend - Contains one or more records for each employee's attendance duration on a given date (i.e. One record for each punch-in / punch-out. Should be equal to indirect + direct labor)
dbo.AggTicket - Contains one or more records for each employee's direct labor duration charged to a particular order number
dbo.ModOrders - Contains one record for each order including the estimated hours, start date, and end date (I will worry about using the start and end dates later for figuring out how many available hours there were on each date)
Here is the code I'm using in my query:
;WITH OrderTots AS
(
SELECT
AggTicket.OrderNo,
SUM(AggTicket.TotDirectHrs) AS TotActHrs
FROM
AIM.dbo.AggTicket
GROUP BY
AggTicket.OrderNo
)
SELECT
d.Date,
t.OrderNo,
o.EstHrs,
OrderTots.TotActHrs,
SUM(t.TotDirectHrs) OVER (PARTITION BY t.TicketDate) AS DaysDirectHrs,
COUNT(a.EmplCode) AS NumEmployees,
SUM(a.TotHrs) AS DaysAttendHrs
FROM
BAS.dbo.DateDimension d
INNER JOIN
AIM.dbo.AggAttend a ON d.Date = a.TicketDate
LEFT OUTER JOIN
AIM.dbo.AggTicket t ON d.Date = t.TicketDate
LEFT OUTER JOIN
AIM.dbo.ModOrders o ON t.OrderNo = o.OrderNo
LEFT OUTER JOIN
OrderTots ON t.OrderNo = OrderTots.OrderNo
GROUP BY
d.Date, t.TicketDate, t.OrderNo, o.EstHrs,
OrderTots.TotActHrs
ORDER BY
d.Date
When I run that query in SQL Server Management Studio 2017, I get the above error.
These are my questions for the community:
Does this error message correctly describe an error in my code?
If so, why is that error an error? (To the best of my knowledge, everything is already contained in either an aggregate function or in the GROUP BY clause...smh)
What is a better way to write this query so that it will function?
Much appreciation to everyone in advance!
I am writing a query in SQL Server 2008 (Express I believe?).
SELECT ##VERSION Will let you know what version you are on.
Column 'AIM.dbo.AggTicket.TotDirectHrs' is invalid in the select list
because it is not contained in either an aggregate function or the
GROUP BY clause.
The problem is with your SUM OVER() statement:
SUM(t.TotDirectHrs) OVER (PARTITION BY t.TicketDate) AS DaysDirectHrs
Here, since you are using the OVER clause, you must include it in the GROUP BY. The OVER clause is used to determine the partitioning and order of a row-set for a window function. So, while you are using an aggregate with SUM you are doing this in a window function. Window functions belong to a type of function known as a 'set function', which means a function that applies to a set of rows. The word 'window' is used to refer to the set of rows that the function works on.
Thus, add t.TotDirectHrs to the GROUP BY
GROUP BY
d.Date, t.TicketDate, t.OrderNo, o.EstHrs,
OrderTots.TotActHrs, t.TotDirectHrs
If this narrows your results into a grouping that you don't want, then you can wrap it in another CTE or use a correlated sub-query. Potentially like the below:
(SELECT SUM(t2.TotDirectHrs) OVER (PARTITION BY t2.TicketDate) AS DaysDirectHrs FROM AIM.dbo.AggTicket t2 WHERE t2.TicketDate = t.TicketDate) as DaysDirectHrs,
EXAMPLE
if object_id('tempdb..#test') is not null
drop table #test
create table #test(id int identity(1,1), letter char(1))
insert into #test
values
('a'),
('b'),
('b'),
('c'),
('c'),
('c')
Given the data set above, suppose we wanted to get a count of all rows. That's simple right?
select
TheCount = count(*)
from
#test
+----------+
| TheCount |
+----------+
| 6 |
+----------+
Here, no GROUP BY is needed because it's implied to group over all columns since no columns are specified in the SELECT list. Remember, GROUP BY groups the SELECT statement results according to the values in a list of one or more column expressions. If aggregate functions are included in the SELECT list, GROUP BY calculates a summary value for each group. These are known as vector aggregates.[MSDN].
Now, suppose we wanted to count each letter in the table. We could do that at least two ways. Using COUNT(*) with the letter column in the select list--or using COUNT(letter) with the letter column in the select list. However, in order for us to attribute the count with the letter, we need to return the letter column. Thus, we must include letter in the GROUP BY to tell SQL Server what to apply the summary table to.
select
letter
,TheCount = count(*)
from
#test
group by
letter
+--------+----------+
| letter | TheCount |
+--------+----------+
| a | 1 |
| b | 2 |
| c | 3 |
+--------+----------+
Now, what if we wanted to return this same count, but we wanted to return all rows as well? This is where window functions come in. The window function works similar to GROUP BY in this case by telling SQL Server the set of rows to apply the aggregate to. Then, it's value is returned for for every row in this window / partition. Thus, it returns a column which is applied to every row making it just like any column or calculated column which is returned form the select list.
select
letter
,TheCountOfTheLetter = count(*) over (partition by letter)
from
#test
+--------+---------------------+
| letter | TheCountOfTheLetter |
+--------+---------------------+
| a | 1 |
| b | 2 |
| b | 2 |
| c | 3 |
| c | 3 |
| c | 3 |
+--------+---------------------+
Now we get to your case where you want to use an aggregate and an aggregate in a window function. Remember that the return of the window function is treated like any other column, thus must be applied in the GROUP BY. Pseudo would look something like this, but window functions aren't allowed in the GROUP BY clause.
select
letter
,TheCount = count(*)
,TheCountOfTheLetter = count(*) over (partition by letter)
from
#test
group by
letter
,count(*) over (partition by letter)
--returns an error
Thus, we must a correlated sub-query or a cte or some other method.
select
t.letter
,TheCount = count(*)
,TheCountOfTheLetter = (select distinct count(*) over (partition by letter) from #test t2 where t2.letter = t.letter)
from
#test t
group by
t.letter
+--------+----------+---------------------+
| letter | TheCount | TheCountOfTheLetter |
+--------+----------+---------------------+
| a | 1 | 1 |
| b | 2 | 2 |
| c | 3 | 3 |
+--------+----------+---------------------+

SQL Server: Find the number of substring occurrences of converted data

I have a huge amount of data and I want to count the number of occurrence for a certain column. What makes this confusing is that it is not just finding the same value of the column, but only part of it.
My table contains TimeStamp in format of (YYYY-MM-DD HH:MM:SS), and I want to apply this SQL command only to YYYY-MM-DD part and count the number of occurrence of each based on that. For example, if my data are as follows:
ID|TimeStamp
--+--------------------
00|2017-08-31 09:00:00
01|2017-08-31 11:00:00
02|2017-08-31 16:30:00
03|2017-08-31 22:00:00
04|2017-09-01 09:00:00
05|2017-09-01 23:40:00
06|2017-09-02 10:30:00
07|2017-09-02 13:00:00
08|2017-09-02 23:00:00
then I want my SQL command to output something like
TimeStamp | Occurrences
-----------+------------
2017-08-31 | 4
2017-09-01 | 2
2017-09-02 | 3
I have been trying to get there from what I have so far but I haven't had luck.
I have this SQL :
SELECT
TimeStamp, COUNT(*)
FROM
myTableName
GROUP BY
TimeStamp
ORDER BY
COUNT(*) DESC -- to sort the occurrence count
but this only counts exactly same timestamps, so this doesn't output what I want it to output. I am also thinking TimeStamp has DateTime type, so I had to convert it to varchar or something first and get the substring of it.
I tried converting the TimeStamp data type to Varchar, and then get the first 10 letters of the string, so
SELECT
COUNT(*) TimeStamp
FROM
myTableName
GROUP BY
(SELECT LEFT(CONVERT(Varchar, TimeStamp, 120), 10))
ORDER BY COUNT(*) DESC
but this causes an error:
Error: Cannot use an aggregate or a subquery in an expression used for the group by list of a GROUP BY clause
Can someone please help me with this? Thank you.
SELECT CAST(TimeStamp as date), COUNT(*)
FROM myTableName
GROUP BY CAST(TimeStamp as date)
ORDER BY 2 DESC

How to forecast count based on a day?

I am new to SQL Server world. I have a table as below:
alert_id | create_date | Status
---------+-------------+---------
1231 | 4/15/2017 | Open
1232 | 4/15/2017 | Open
1234 | 4/15/2017 | Closed
1235 | 4/16/2017 | Open
All of these alerts should be closed in 30 days. I need to get a forecast report which shows how many alerts are open for past 30 days.
I would like to write a select query whose output would be 2 columns. First would be Date and 2nd would be count. The date column should display all the dates for next 30 days and Count column should display the number of records which are due to expire on that day. Something like below would work. Please assist.
date | Count
----------+---------
5/15/2017 | 2
5/16/2017 | 3
5/17/2017 | 0
5/18/2017 | 0
.
.
.
6/14/2017 | 0
This is a job for GROUP BY and date arithmetic. In MySQL:
SELECT DATE(create_date) + INTERVAL 30 DAY expire_date, COUNT(*) num
FROM tbl
WHERE status = 'Open'
GROUP BY DATE(create_date)
DATE(create_date) + INTERVAL 30 DAY gets you the create date values with thirty days added.
GROUP BY(create_date) groups your data by values of your create date, truncated to midnight.
And, COUNT(*) goes with GROUP BY to tell you how many records in each group.
Edit In recent versions of SQL Server (MS)
SELECT DATEADD(day, 30, CAST(create_date AS DATE)) expire_date, COUNT(*) num
FROM tbl
WHERE status = 'Open'
GROUP BY CAST(create_date AS DATE)
Notice, please, that date arithmetic varies between make and model of SQL server software. That's why you get hassled by Stack Overflow users in comments when you use more than one tag like [oracle] [mysql] [sql-server] on your questions.
Cool, huh? You should read up on aggregate queries, sometimes called summary queries.
You're not going to get the missing dates with zeros by them. That's quite a bit harder to do with SQL.

How to add "missing" columns in a column group in reporting services?

I'm trying to create a report that displays for each months of the year the quantity of goods sold.
I have a query that returns the list of goods sold for each month, it looks something like this :
SELECT Seller.FirstName, Seller.LastName, SellingHistory.Month, SUM(SellingHistory.QuantitySold)
FROM SellingHistory JOIN Seller on SellingHistory.SellerId = Seller.SellerId
WHERE SellingHistory.Year = #Year
GOUP BY Seller.FirstName, Seller.LastName, SellingHistory.Month
What I want to do is display a report that has a column for each months + a total column that will display for each Seller the quantity sold in the selected month.
Seller Name | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec | Total
What I managed to do is using a matrix and a column group (group on Month) to display the columns for existing data, if I have data from January to March, it will display the 3 first columns and the total. What I would like to do is always display all the columns.
I thought about making that by adding the missing months in the SQL request, but I find that a bit weird and I'm sure there must be some "cleanest" solution as this is something that must be quite frequent.
Thanks.
PS: I'm using SQL Server Express 2008
SELECT Seller.Id,
SUM(CASE WHEN SellingHistory.Month = 'Jan' THEN SellingHistory.QuantitySold END ) AS Jan,
SUM(CASE WHEN SellingHistory.Month = 'Feb' THEN SellingHistory.QuantitySold END ) AS Feb,
...
GROUP BY Seller.Id
You can also use PIVOT(double check syntax, I think the following query is ok, but I haven't worked with transact sql for a while) :
SELECT Seller.Id, Jan, Feb, ...
FROM ...
PIVOT (SUM(SellingHistory.QuantitySold) FOR SellingHistory.Month IN (
[Jan],[Feb],....)) AS t;
To do this in T-SQL you want a PIVOT, there is an example using months midway down the page here.

Resources