Display only needed results in SQL - sql

I need a little assistance in finishing this query. Here is what I have so far:
select
(select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 17) AS "Files on SFTP"
,
(select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 12) AS "Files Pass"
,
((select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 17)
-
(select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 12)) AS "Diff"
This is going to give me 3 columns of results. First result will be a number, second will be a number and the third will be the diff. There may even be a better way to write this but I'm still a novice. Hint: There is an entry in the DB for each state:
fileName |dateTime | stateID
--------+---------+-----------------+---------
abc.pdf | 2013-12-17 12:03:14.597 | 17
abc.pdf | 2013-12-17 12:06:23.096 | 12
xyz.pdf | 2013-12-17 12:09:16.583 | 17
xyz.pdf | 2013-12-17 12:10:19.823 | 12
Anyways for the finale...
I need to have a 4th column or a separate query (possible to UNION?) that pulls the fileNames based off the results in the diff.
Hypothetically if the diff is 40, the 4th column or separate query should list the 40 names. At times the diff may be negative so again hypothetically speaking if its -40 it should list the 40 names.
Assistance is greatly appreciated. Thank you!

You can greatly simplify your query using conditional aggregation:
select sum(case when dateTime > cast(getdate() as date) and stateId = 17 then 1 else 0
end) as "Files on SFTP",
sum(case when dateTime > cast(getdate() as date) and stateId = 12 then 1 else 0
end) AS "Files Pass",
(sum(case when dateTime > cast(getdate() as date) and stateId = 17 then 1 else 0
end) -
sum(case when dateTime > cast(getdate() as date) and stateId = 12 then 1 else 0
end)
) as diff
from PDFFile;
To get the list of files that are in the first group but not the second requires a bit more logic. The problem is that the unit of aggregation is at the file level.
select PDFFile
from PDFFile
group by PDFFile
having sum(case when dateTime > cast(getdate() as date) and stateId = 17 then 1 else 0
end) > 0 and
sum(case when dateTime > cast(getdate() as date) and stateId = 12 then 1 else 0
end) = 0;
Each part of the having clause counts the number of rows -- for each file -- that match the two conditions. You want at least one row that matches the first condition (hence > 0) and no rows that match the second (= 0).

This type of "combine row data into one column" question comes up quite a lot on Stack Overflow and although it has its place it's often easier and more efficient to solve the problem in another way.
For example, it's a lot easier to ask SQL to "give me all the filenames where stateid = 17", return them to your app and then get the app to display them. It may also be that your user doesn't want to see them until there is a particular summary line that is of interest to them that they need to drill down into further. Think of email as an example - you may only need to look at the 30 character subject line and know you don't need to download the 1Mb email body.
For your first question though there is a lot easier (and more efficient) way to write your query. Note that this example is untested
select
sum(case when stateId = 17 then 1 else 0 end) as "Files on SFTP",
sum(case when stateId = 12 then 1 else 0 end) as "Files Pass",
sum(case when stateId = 17 then 1 else 0 end) -
sum(case when stateId = 12 then 1 else 0 end) as "Diff",
from
PdfFile
where
datetime > getdate()
I'm using CASE here to prevent having to do three separate sub-queries. Sub-queries are inefficient. CASE isn't great but it's faster than sub-queries. I've also placed your datetime check at the bottom of the query as a WHERE as it was common to each of your checks.

Related

Using COUNT CASE WHEN MONTH Statement in MariaDB 10.2.15

I created a query to calculate the Amount of Id in a table using COUNT, CASE, WHEN and MONTH ..
Code:
SELECT
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN LogsFormatted.Id ELSE 0 END ) AS '1',
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 2 THEN LogsFormatted.Id ELSE 0 END ) AS '2'
FROM
HrAttLogsFormatted AS LogsFormatted
WHERE
LogsFormatted.DateIn BETWEEN '2019-01-01' AND '2019-02-31'
AND LogsFormatted.Late != ''
Output :
| 1 | 2 |
| 1378 | 1378 |
The output I want to make is to calculate the Id in each month, namely Month 1 and Month 2
| 1 | 2 |
| 792 | 586 |
The data above is a fact
Using the above query instead adds up between the results of calculating month 1 and month 2
You should be counting NULL when the criteria in your CASE expression does not match. Also, I prefer counting 1 unless you really want to the count the Ids themselves. This version should work:
SELECT
COUNT(CASE WHEN MONTH(lf.DateIn) = 1 THEN 1 END) AS '1',
COUNT(CASE WHEN MONTH(lf.DateIn) = 2 THEN 1 END) AS '2'
FROM HrAttLogsFormatted AS lf
WHERE
lf.DateIn BETWEEN '2019-01-01' AND '2019-02-31' AND
lf.Late != '';
Note carefully that the current counts you are seeing sum up to the individual counts, that is:
1378 = 792 + 586
The reason for this is the the COUNT function "counts" any non NULL value as 1, and any NULL value as zero. Your current CASE expression will always count 1, for every record in the table.
remove else part from case when expression - if you use else with 0 then count takes that also in consideration which gives u actually wrong ouput
SELECT
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN LogsFormatted.Id END ) AS '1',
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 2 THEN LogsFormatted.Id END ) AS '2'
FROM
HrAttLogsFormatted AS LogsFormatted
WHERE
LogsFormatted.DateIn BETWEEN '2019-01-01' AND '2019-02-31'
AND LogsFormatted.Late != ''
If you are using MariaDB, I would just use SUM() with a boolean:
SELECT SUM( MONTH(lf.DateIn) = 1 ) as month_1,
SUM( MONTH(lf.DateIn) = 2 ) as month_2
FROM HrAttLogsFormatted lf
WHERE lf.DateIn >= '2019-01-01' AND
lf.DateIn < '2020-01-01' AND
lf.Late <> '';
This assumes that the id that you are counting is never NULL (a reasonable assumption for an id).
Note other changes:
The column aliases do not need to be escaped. Don't use non-standard names, unless you need them -- for some reason -- for downstream processing.
This uses a shorter table alias, so the query is easier to write and to read.
The date comparisons use inequalities, so this works both for dates and datetimes.
<> is the standard SQL comparison operator for inequality.

SQL - Group data with same ID and Date that has been to every Machine but has a different Name

I am trying to create a query that will group data by CT ID and Date that have all 3 MachineID's (1, 10, and 20) and at least one different Sawing Pattern Name.
This Image shows a highlighted example of the data I'm trying to get back and the code i'm currently using
I'm trying to only show data similar to the highlighted rows in the image (CT ID 501573833) and exclude the data in the rows around it where the Sawing Pattern Name is the same at all 3 MachineID's.
Your description suggests group by and having. The conditions you describe can all go in the having clause:
select ct_id, date
from t
group by ct_id, date
having sum(case when machineid = 1 then 1 else 0 end) > 0 and
sum(case when machineid = 10 then 1 else 0 end) > 0 and
sum(case when machineid = 20 then 1 else 0 end) > 0 and
min(sawing_pattern_name) <> max(sawing_pattern_name)
Seems to me that an EXISTS could be useful here.
SELECT
[CT ID],
[MachineID],
[Sawing Pattern name],
[Time],
CAST([Time] AS DATE) AS [Date]
FROM [DataCollector].[dbo].[Maxicut] t
WHERE EXISTS
(
SELECT 1
FROM [DataCollector].[dbo].[Maxicut] d
WHERE d.[CT ID] = t.[CT ID]
AND CAST(d.[Time] AS DATE) = CAST(t.[Time] AS DATE)
AND d.[MachineID] != t.[MachineID]
AND REPLACE(d.[Sawing Pattern name],',','') != REPLACE(t.[Sawing Pattern name],',','')
);

Aging - Calendar and Business Days Different Records of Data

I am currently using SQL Server 2012 and I have an aging question. I tried to include some sample data below with 4 records.
FileNumber FileType CompletedDate
90440 Internal 8/11/2017
90440 Strategy NULL
90441 Internal 8/10/2017
90441 Strategy NULL
A Strategies' aging is calculated from the Internal FileType's Completed Date all the way up to the Strategy FileType's Completed Date. Every FileNumber can have multiple FileTypes associated with it. If the Strategy's Completed Date is NULL, then it would be from Internal FileType's Completed Date all the way up to today's date or GETDATE().
So I'm trying to show Count of Pending Strategies and their current aging in Business and Calendar Days...so essentially I would want the data to return like this.
File Number FileType AgeBusiness AgeCalendar
90440 Strategy 2 4
90441 Strategy 3 5
Any clue on how I would go about this? Any help is appreciated.
Something like this may work but I'm not 100% sure since it depends on what sort of Filetypes are in the table, whether you are accounting for holidays, what happens when Strategy (or whatever is the latest file type) has a value for completed date.
;WITH Internal AS
(
select FileNumber, FileType,
DATEDIFF(day,CompletedDate,getdate())- (datediff(wk, CompletedDate, getdate()) * 2) -
case when datepart(dw, CompletedDate) = 1 then 1 else 0 end +
case when datepart(dw, getdate()) = 1 then 1 else 0 end as AgeBusiness,
DATEDIFF(day,CompletedDate,getdate()) as AgeCalendar
from #test
where FileType = 'Internal'
)
select t.FileNumber, t.FileType, i.AgeBusiness, i.AgeCalendar
from #test t
inner join Internal i on
(t.FileNumber = i.FileNumber)
where CompletedDate is null
the problem with the above is it wouldn't show a row if Strategy (or whatever is the latest file type) HAS a completed date. Therefore you may want something like
;WITH Internal AS
(
select FileNumber, FileType,
DATEDIFF(day,CompletedDate,getdate())- (datediff(wk, CompletedDate, getdate()) * 2) -
case when datepart(dw, CompletedDate) = 1 then 1 else 0 end +
case when datepart(dw, getdate()) = 1 then 1 else 0 end as AgeBusiness,
DATEDIFF(day,CompletedDate,getdate()) as AgeCalendar
from #test
where FileType = 'Internal'
), Latest AS
(
select FileNumber, FileType, RANK() OVER
(PARTITION BY FileNumber ORDER BY COALESCE(CompletedDate,GETDATE()) DESC) AS rnk
from #test
)
select l.FileNumber, l.FileType, i.AgeBusiness, i.AgeCalendar
from Latest l
inner join Internal i on
(l.FileNumber = i.FileNumber)
where rnk = 1
where it would show the row regardless of if it's null or not, but at the expense of adding a RANK() which is more intensive.
You don't need a CTE but I included it to make it more readable.
WITH DateRanges AS (
SELECT yt.FileNumber
,yt.FileType
,SELECT TOP 1 CompletedDate FROM YourTable WHERE yt.FileNumber = FileNumber AND FileType = 'Internal' AS InternalCompletedDate
,CASE WHEN yt.CompletedDate IS NULL THEN CAST(GETDATE() AS DATE) ELSE yt.CompletedDate END AS StrategyCompletedDate -- I forgot the NULL part too...
FROM YourTable yt
WHERE yt.FileType = 'Strategy'
)
SELECT FileNumber
,FileType
,(DATEDIFF(dd,InternalCompletedDate, StrategyCompletedDate) + 1)
-(DATEDIFF(wk, InternalCompletedDate, StrategyCompletedDate) * 2)
-(CASE WHEN DATENAME(dw, InternalCompletedDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, StrategyCompletedDate) = 'Saturday' THEN 1 ELSE 0 END) AS AgeBusiness
,DATEDIFF(dd, InternalCompletedDate, StrategyCompletedDate) AS AgeCalendar
FROM DateRanges
Edit: Should be good now. I rushed it a bit.

How to return count in 2 columns?

I have this query. It should return Count for both AWARDED (1) and NOT AWARDED(0) works from works table.
Select Count(w.WorkID)as Total, w.IsAwarded, org.OrganizationName
From Works w
Inner Join MC_MemberShip.Membership.Organization org
ON org.OrganizationID= w.Organization_ID
where Convert(varchar(11), w.OpeningDate) >= Convert(varchar(11), #FromDate)
and Convert(varchar(11), w.OpeningDate) < DATEADD(day, 1, Convert(varchar(11), #ToDate))
and w.IsActive=1 and
ISNULL(w.IsAwarded,0)= 0 and w.Organization_ID= case when #OrgID= -1 then w.Organization_ID else #OrgID end
group by org.OrganizationName, w.IsAwarded
Now this query returns Total count for NOT AWARDED i.e. 0 only but i want to return count for AWARDED too in same query.
Organization TotalAwardedWorks TotalNotAwardedWorks
Town 1 1 2
Town 2 44 33
Your query should look something like this:
select org.OrganizationName,
Count(*) as Total,
sum(case when w.IsAwarded = 0 or w.IsAwarded is null then 1 else 0 end) as TotalNotAward,
sum(case when w.IsAwarded = 1 then 0 else 1 end) as TotalAward
from Works w Inner Join
MC_MemberShip.Membership.Organization org
on org.OrganizationID = w.Organization_ID
where w.OpeningDate >= #FromDate and
w.OpeningDate < dateadd(day, 1, #ToDate) and
w.IsActive = 1 and
(w.Organization_ID = #OrgId or #OrgID= -1)
group by org.OrganizationName;
Notes:
Do not convert dates to strings to perform comparisons. That is just perverse.
Generally, the use of case in the where clause is discouraged. The logic is more clearly represented using or.
You can get what you want by using case to put conditions in the aggregation functions.

select query output not as expected

i need one single query which will give result like the one i give below
createddate recordcount acceptdate submitdate createddate
27-MAR-16 24 36 11
28-MAR-16 79 207 58
for reference i am providing some queries which i want to merge into one single query
select trim(date_created) createddate,count(*) recordcount
from man
where status IN ('CREATED')and date_created>sysdate-15
group by trim(date_created) ORDER BY TO_DATE(createddate,'DD/MM/YYYY');
this query will result like the following.
createddate recordcount
27-MAR-16 11
28-MAR-16 58
the second query
select trim(DATE_SUB) submitdate,count(*) recordcount
from man
where status IN ('SUBMITTED')and DATE_SUB>sysdate-15
group by trim(date_sub) ORDER BY TO_DATE(submitdate,'DD/MM/YYYY');
result of this query is like
submitdate recordcount
27-MAR-16 36
28-MAR-16 207
and the third query is like -
select trim(DATE_PUB) acceptdate,count(*) recordcount
from man
where status IN ('ACCEPTED')and DATE_PUB>sysdate-15
group by trim(DATE_PUB) ORDER BY TO_DATE(acceptdate,'DD/MM/YYYY');
acceptdate recordcount
27-MAR-16 24
28-MAR-16 79
how can i merger these three query so that i can get count for all in single query?which will give me result like
createddate recordcount acceptdate submitdate createddate
27-MAR-16 24 36 11
28-MAR-16 79 207 58
Your first query where clause has date but second query where clause has DATE_P.
Try like this
SELECT Trim(date) createddate,
COUNT(*) recordcount,
SUM(case when status = 'A' then 1 else 0 end) as a,
SUM(case when status = 'S' then 1 else 0 end) as s,
SUM(case when status = 'C' then 1 else 0 end) as c,
SUM(case when status = 'R' then 1 else 0 end) as r
FROM man
WHERE status IN ('A','S','C','R')and date >sysdate-15
GROUP BY trim(date) ORDER BY createddate;
You seem to want to get counts for each status type, for each day. The first step is generate all the dates you're interested in, which you can do with:
select trunc(sysdate) + 1 - level as dt
from dual
connect by level <= 15;
You can then (outer) join to your actual table where any of the three date columns match a generated date, and expand your case conditions to check which one you're looking at:
with t as (
select trunc(sysdate) + 1 - level as dt
from dual
connect by level <= 15
)
select t.dt,
count(*) as recordcount,
count(case when status = 'ACCEPTED' and trunc(m.date_pub) = t.dt
then 1 end) as acceptdate,
count(case when status = 'SUBMITTED' and trunc(m.date_sub) = t.dt
then 1 end) as submitdate,
count(case when status = 'CREATED' and trunc(m.date_created) = t.dt
then 1 end) as createddate
from t
left join man m
on (m.date_pub >= t.dt and m.date_pub < t.dt + 1)
or (m.date_sub >= t.dt and m.date_sub < t.dt + 1)
or (m.date_created >= t.dt and m.date_created < t.dt + 1)
group by t.dt
order by t.dt;
I've used range checks for the join conditions - it isn't clear if all your date columns are set at midnight, but it's safer to assume they might have other times and you cant everything from the matching day.
Each of the three count results is now only of those rows which match the status and where the specific date column matches, which I think is what you want. I've used trunc() here instead of a range comparison, as it doesn't have the potential performance penalty you can see in the where clause (from it potentially stopping an index being used).
This may throw out your recordcount though, depending on your actual data, as that will include rows that now might not match any of the case conditions. You can repeat the case conditions, or use an inline view to calculate the total of the three individual counts, depending on what you want it to include and what will be the easiest for you to maintain. If those are the only three statuses in your table then it may be OK with count(*) but check it gets the value you expect.