SQL- HAVING Clause Issue - sql

I'm trying to use the MAX() function as a subquery to filter results of my greater query by the last couple of whole years. I realized this was a little bit more complicated than what I initially thought it would need so I created a much simpler query to test out what I would need to do with the HAVING clause in order to get it to work.
I've checked these two posts but it didn't really help (SQL, HAVING clause explained, SQL: HAVING clause)
I don't think my understanding of the HAVING clause is correct because I'm not sure why it's not working. Would someone be able to help and explain?
Note: [Fiscal Year] is NVARCHAR so I figured converting it to INT might work.
SELECT DISTINCT
D.[FISCAL YEAR]
FROM [Dates] AS D
GROUP BY D.[Fiscal Year]
HAVING CONVERT(INT,D.[Fiscal Year]) >= MAX(CONVERT(INT,D.[FISCAL YEAR])) -2
These are my results:
(No column name)
2015
2014
2013
2012
2016
These are the results I should get:
(No column name)
2015
2014
2016

I think this could be done better in the WHERE clause. This solution isn't optimal, but here's what you can do.
SELECT
DISTINCT D.[FISCAL YEAR]
FROM [Dates] AS D
WHERE CONVERT(INT,d.[Fiscal Year])
>= (SELECT MAX(CONVERT(INT,[Fiscal Year])) -2 from Dates)

The problem you are having is you are grouping by the year, thus the max will always be the same as the year for that group.
Here's one option using window functions:
select distinct fiscalyear
from (
select fiscalyear, max(fiscalyear) over () maxfiscalyear
from dates
) t
where fiscalyear >= maxfiscalyear - 2
I don't see the need to use the convert function here either, but if you need to, you can add it back.
SQL Fiddle Demo

Related

get the latest records

I am currently still on my SQL educational journey and need some help!
The query I have is as below;
SELECT
Audit_Non_Conformance_Records.kf_ID_Client_Reference_Number,
Audit_Non_Conformance_Records.TimeStamp_Creation,
Audit_Non_Conformance_Records.Clause,
Audit_Non_Conformance_Records.NC_type,
Audit_Non_Conformance_Records.NC_Rect_Received,
Audit_Non_Conformance_Records.Audit_Num
FROM Audit_Non_Conformance_Records
I am trying to tweak this to show only the most recent results based on Audit_Non_Conformance_Records.TimeStamp_Creation
I have tried using MAX() but all this does is shows the latest date for all records.
basically the results of the above give me this;
But I only need the result with the date 02/10/2019 as this is the latest result. There may be multiple results however. So for example if 02/10/2019 had never happened I would need all of the idividual recirds from the 14/10/2019 ones.
Does that make any sense at all?
You can filter with a subquery:
SELECT
kf_ID_Client_Reference_Number,
TimeStamp_Creation,
Clause,
NC_type,
NC_Rect_Received,
Audit_Num
FROM Audit_Non_Conformance_Records a
where TimeStamp_Creation = (
select max(TimeStamp_Creation)
from Audit_Non_Conformance_Records
)
This will give you all whose TimeStamp_Creation is equal to the greater value available in the table.
If you want all records that have the greatest day (exluding time), then you can do:
SELECT
kf_ID_Client_Reference_Number,
TimeStamp_Creation,
Clause,
NC_type,
NC_Rect_Received,
Audit_Num
FROM Audit_Non_Conformance_Records a
where cast(TimeStamp_Creation as date) = (
select cast(max(TimeStamp_Creation) as date)
from Audit_Non_Conformance_Records
)
Edit
If you want the latest record per refNumber, then you can correlate the subquery, like so:
SELECT
kf_ID_Client_Reference_Number,
TimeStamp_Creation,
Clause,
NC_type,
NC_Rect_Received,
Audit_Num
FROM Audit_Non_Conformance_Records a
where TimeStamp_Creation = (
select max(TimeStamp_Creation)
from Audit_Non_Conformance_Records a1
where a1.refNumber = a.refNumber
)
For performance, you want an index on (refNumber, TimeStamp_Creation).
If you want the latest date in SQL Server, you can express this as:
SELECT TOP (1) WITH TIES ancr.kf_ID_Client_Reference_Number,
ancr.TimeStamp_Creation,
ancr.Clause,
ancr.NC_type,
ancr.NC_Rect_Received,
ancr.Audit_Num
FROM Audit_Non_Conformance_Records ancr
ORDER BY CONVERT(date, ancr.TimeStamp_Creation) DESC;
SQL Server is pretty good about handling dates with conversions, so I would not be surprised if this used an index on TimeStamp_Creation.

Joining multiple tables returning duplicates

I am trying the following select statement including columns from 4 tables. But the results return each row 4 times, im sure this is because i have multiple left joins but i have tried other joins and cannot get the desired result.
select table1.empid,table2.name,table2.datefrom, table2.UserDefNumber1, table3.UserDefNumber1, table4.UserDefChar6
from table1
inner join table2
on table2.empid=table1.empid
inner join table3
on table3.empid=table1.empid
inner join table4
on table4.empid=table1.empid
where MONTH(table2.datefrom) = Month (Getdate())
I need this to return the data without any duplicates so only 1 row for each entry.
I would also like the "where Month" clause at the end look at the previous month not the current month but struggling with that also.
I am a bit new to this so i hope it makes sense.
Thanks
If the duplicate rows are identical on each column you can use the DISTINCT keyword to eliminate those duplicates.
But I think you should reconsider your JOIN or WHERE clause, because there has to be a reason for those duplicates:
The WHERE clause hits several rows in table2 having the same month on a single empid
There are several rows with the same empid in one of the other tables
both of the above is true
You may want to rule those duplicate rows out by conditions in WHERE/JOIN instead of the DISTINCT keyword as there may be unexpected behaviour when some data is changing in a single row of the original resultset. Then you start having duplicate empids again.
You can check if a date is in the previous month by following clause:
date BETWEEN dateadd(mm, -1, datefromparts(year(getdate()), month(getdate()), 1))
AND datefromparts(year(getdate()), month(getdate()), 1)
This statment uses DATEFROMPARTS to create the beginning of the current month twice, subtract a month from the first one by using DATEADD (results in the beginning of the previous month) and checks if date is between those dates using BETWEEN.
If your query is returning duplicates, then one or more of the tables have duplicate empid values. This is a data problem. You can find them with queries like this:
select empid, count(*)
from table1
group by empid
having count(*) > 1;
You should really fix the data and query so it returns what you want. You can do a bandage solution with select distinct, but I would not usually recommend that. Something is causing the duplicates, and if you do not understand why, then the query may not be returning the results you expect.
As for your where clause. Given your logic, the proper way to express this would include the year:
where year(table2.datefrom) = year(getdate()) and
month(table2.datefrom) = month(Getdate())
Although there are other ways to express this logic that are more compatible with indexes, you can continue down this course with:
where year(table2.datefrom) * 12 + month(table2.datefrom) = year(getdate()) * 12 + Month(Getdate()) - 1
That is, convert the months to a number of months since time zero and then use month arithmetic.
If you care about indexes, then your current where clause would look like:
where table2.datefrom >= dateadd(day,
- (day(getdate) - 1),
cast(getdate() as date) and
table2.datefrom < dateadd(day,
- (dateadd(month, 1, getdate()) - 1),
cast(dateadd(month, 1, getdate()) as date)
Eliminate duplicates from your query by including the distinct keyword immediately after select
Comparing against a previous month is slightly more complicated. It depends what you mean:
If the report was run on the 23rd Jan 2015, would you want 01/12/2014-31/12/2014 or 23/12/2014-22/01/2015?

Query last row from 2012 + all rows from 2013 + first row from 2014 using Rails(ActiveRecord) - Best Practice

I finding best practice solution to fetch rows from database using this conditions:
Last row from year 2012
All rows from year 2013
and first row from 2014
Is this solvable by one query in Rails.
I have no idea how to do it by one query but only using 3 queries.
Thanks.
This is one way to do it. I'm not saying it's the best or super optimized though, depends on your data and indexes.
#measurements = Measurement.find_by_sql(
%{select * from measurements
where date_part('year', date) = 2013
or date = (select max(date) from rounds where date_part('year', date) = 2012)
or date = (select min(date) from rounds where date_part('year', date) = 2014)
order by date})
Edit: I see you're using sqlite, this works for Postgres but should be easily portable.
This has worked for me, only it was with postgres and YEAR(dt) was date_part('year',dt). Plain and simple...
Measurement.where("YEAR(dt)=2013 or dt=? or dt=?",
Measurement.where("YEAR(dt)=2012").maximum(:dt),
Measurement.where("YEAR(dt)=2014").minimum(:dt))

How to get month name out of date field

I'm trying to build a query that gives me a year month field with month names instead of the numbers. The output should be similar to this:
Year/month field | Sum field
Januari 2014 100
Februari 2014 12300
Maart 2014 3234
April 2014 4964
I've searched for the way to get the the month name but I must be doing something wrong because I constantly get the error that datename is not a function or an sql error 1064. This i what i've tried:
SELECT id,
YEAR(report_date) AS YEAR,
MONTH(report_date) AS MONTH,
SELECT DATENAME(MONTH, (MONTH(report_date))) ,
CONVERT(VARCHAR(3), DATENAME(MONTH, report_date)) SUM(num) AS participants_month
FROM participants
WHERE unit_id = 10
GROUP BY unit_id,
MONTH(report_date)
ORDER BY report_date ASC;
Tried to make this question as clear as possible. If something isn't clear tell me and I'll try to explain it. Really hope someone could point me out in the right direction.
In SQL Server you use DATENAME(month, date):
SQL Server Fiddle
But in MySQL you use MONTHNAME(date):
MySQL Fiddle
From the error you are receiving I guess you do not use SQL Server as your database management system, so in MySQL you should use MONTHNAME(date).
From the error, I suppose, you are using MySQL.But the DATENAME() is Transact-SQL function.
In MySQL, you can use
MONTH(yourdate)
instead

SQL: Can GROUP BY contain an expression as a field?

I want to group a set of dated records by year, when the date is to the day. Something like:
SELECT venue, YEAR(date) AS yr, SUM(guests) AS yr_guests
FROM Events
...
GROUP BY venue, YEAR(date);
The above is giving me results instead of an error, but the results are not grouping by year and venue; they do not appear to be grouping at all.
My brute force solution would be a nested subquery: add the YEAR() AS yr as an extra column in the subquery, then do the grouping on yr in the outer query. I'm just trying to learn to do as much as possible without nesting, because nesting usually seems horribly inefficient.
I would tell you the exact SQL implementation I'm using, but I've had trouble discovering it. (I'm working through the problems on http://www.sql-ex.ru/ and if you can tell what they're using, I'd love to know.) Edited to add: Per test in comments, it is probably not SQL Server.
Edited to add the results I am getting (note the first two should be summed):
venue | yr | yr_guests
1 2012 15
1 2012 35
2 2012 12
1 2008 15
I expect those first two lines to instead be summed as
1 2012 50
Works Fine in SQL Server 2008.
See working Example here: http://sqlfiddle.com/#!3/3b0f9/6
Code pasted Below.
Create The Events Table
CREATE TABLE [Events]
( Venue INT NOT NULL,
[Date] DATETIME NOT NULL,
Guests INT NOT NULL
)
Insert the Rows.
INSERT INTO [Events] VALUES
(1,convert(datetime,'2012'),15),
(1,convert(datetime,'2012'),35),
(2,convert(datetime,'2012'),12),
(1,convert(datetime,'2008'),15);
GO
-- Testing, select newly inserted rows.
--SELECT * FROM [Events]
--GO
Run the GROUP BY Sql.
SELECT Venue, YEAR(date) AS yr, SUM(guests) AS yr_guests
FROM Events
GROUP BY venue, YEAR(date);
See the Output Results.
VENUE YR YR_GUESTS
1 2008 15
1 2012 50
2 2012 12
it depends of your database engine (or SQL)
to be sure (over different DB Systems & Versions), make a subquery
SELECT venue, theyear, SUM(guests) from (
SELECT venue, YEAR(date) AS theyear, guest
FROM Events
)
GROUP BY theyear
you make a subtable of
venue, date as theyear, guest
aaaa, 2001, brother
aaaa, 2001, bbrother
bbbb, 2001, nobody
... and so on
and then
count them