SQL SELECT with condition and unique name - sql

Say I have the following table. If I want to return ONLY the inactive rows (Active = 0) but disregard a field that has the same FileName. For instance, the query should only return Helper0990329 since it is inactive and has no other filename in another row that is the same.
VinnyVincenzo1345090457296 should not be included in the results.
PATH | FileName | Active
C:\Vinny\ VinnyVincenzo1345090457296.mp3 0
C:\Vinny\ VinnyVincenzo1345090457296.mp3 1
C:\Vinny\ VinnyVincenzo1345137702505.mp3 1
C:\Helper\ Helper0990329.mp3 0
I tried the following but I ended up deactivating (and later deleting) files that I shoulnt have:
SELECT
[Path],
[FileName]
FROM [Flows].[dbo].[Flows_Flows]
Where [Active] = '0' AND [Created] > '8/18/2012'
Group By Path, FileName
Having count(FileName) = 1
GO

Assuming you are using SQL 2005 or greater, you can use ranking functions to solve this:
select *
from (SELECT [Path], [FileName],
count(*) over (partition by path, filename) as numFiles
FROM [Flows].[dbo].[Flows_Flows]
Where [Created] > '8/18/2012'
) t
where active = 0 and numfiles = 1
The problem with our query is that the WHERE clause was evaluated before the HAVING clause. So, the active rows were never seen.
Or, you can do this with a simple group by but a more complicated HAVING clause:
SELECT [Path], [FileName]
FROM [Flows].[dbo].[Flows_Flows]
Where [Created] > '8/18/2012'
Group By Path, FileName
Having count(FileName) = 1 and
sum(case when Active = '0' then 1 else 0 end) = 1

Try it by counting its number of instance inside SubQuery
SELECT [Path], [FileName]
FROM Flows_Flows a
INNER JOIN
(
SELECT [FileName],
COUNT([FileName]) TotalCount
FROM Flows_Flows
GROUP BY [FileName]
) b ON a.[FileName] = b.[FileName]
WHERE a.[Active] = 0 AND
a.[Created] > '8/18/2012' AND
b.TotalCount = 1

You can use a LEFT OUTER JOIN and when the join finds a duplicate row, exclude it (dup.path is null).
SELECT
f.[Path],
f.[FileName]
FROM [Flows].[dbo].[Flows_Flows] f
LEFT OUTER JOIN [Flows].[dbo].[Flows_Flows] dup on f.Path = dup.Path and dup.Active = '1' --Active must be set otherwise, we found ourselves
Where f.[Active] = '0' AND f.[Created] > '8/18/2012'
AND dup.Path is null -- here we exculde all rows that have a duplicate
The advantage being that this solution has no sub query nor aggregate. That being said, not having looked at the query plan, I cannot say if it is indeed faster. (I also just prefer this syntax, I suppose.)

I'm guessing you have an id field as well right?
I'm also assuming that you dont necessarily want only ones that aren't duplicate rather ones users that don't have ANY active entries.
You could use IN in the WHERE clause.
example:
SELECT Path, FileName FROM Flows_Flows WHERE Active = 0 AND id NOT IN
( SELECT id FROM Flows_Flows WHERE Active = 1 )

Related

Use rownum to always get non null value

select case when (CUST.ADDRESS_TYPE='OFFICE') then
(Select MOBILE
FROM cust_table CUST
where CID = Deal.CID
and ADDRESS_TYPE = 'CURRES'
and rownum = 1)
else
CUST.MOBILE
end as MOBILE
FROM cust_table CUST
RIGHT OUTER JOIN (SELECT CID CID
, WNAME
, APPLICANT_TYPE
FROM deal_table ) DEAL
ON DEAL.CID = CUST.CID
AND APPLICANT_TYPE = 'P'
and mailing_add = 'true'
WHERE WNAME='22135'
and rownum = 1
#MOBILE#
NULL
647432923
OR
#MOBILE#
74238423
NULL
This query returns a column named 'MOBILE ' with two rows, one of the entries being always null when I dont use rownum = 1 at the end, but if I put rownum = 1 towards the end then in some cases it returns null value and in some cases non null value. How can I use rownum so that the query always returns non null value.
Do not use rownum for this. rownum will give you the number of the row after your query has been run. Use a properly defined ORDER BY clause to get your NULLS at the end and only fetch first row.
<your query>
WHERE wname='22135'
ORDER BY mobile NULLS LAST
FETCH FIRST 1 ROWS ONLY

Assign null if subquery retrieves multiple records. How can it be done?

I have the following query. I simplified it for demo purpose. I am using SQL Server - t-sql
Select tm.LocID = (select LocID from tblLoc tl
where tl.LocID = tm.LodID )
from tblMain tm
if the subquery returns multiple records, I like to assign tm.LocID to null else if there is only 1 record returned then assign it to tm.LocID. I am looking for a simple way to do this. Any help would be appreciated.
One way I can see is to have a CASE statement and check if (Count * > 1 ) then assign null else return the value but that would require a select statement within a select statement.
You have the right idea about using a case expression for count(*), but it will not require another subquery:
SELECT tm.LocID = (SELECT CASE COUNT(*) WHEN 1 THEN MAX(LocID) END
FROM tblLoc tl
WHERE tl.LocID = tm.LodID )
FROM tblMain tm
or just use a HAVING clause, like
Select tm.LocID = (select LocID from tblLoc tl
where tl.LocID = tm.LodID
group by locID
having count(*) = 1)
)
from tblMain tm
Your query above (and many of the other answers here) is a correlated subquery which will be very slow since it performs a separate aggregation query on each record. This following will address both your problem and potentially perform a bit better since the count happens in a single pass.
SELECT
CASE
WHEN x.locid IS NOT NULL THEN x.locid
ELSE NULL
END
FROM tblMain m
LEFT JOIN (
SELECT
locid
FROM tblLoc
GROUP BY locid
HAVING COUNT(1) = 1
) x
ON x.locid = m.locid
;
The above is in Postgres syntax (what I'm familiar with) so you would have to make it TSQL compatible.

SQL Calculate Percentage on 2 columns

I have a problem, I need to calculate the percentage between 2 different columns. Unfortunately I can't get it to work and when I run this all I get is "Invalid column name 'CountOfPlannedVisits'" & "Invalid column name 'CountOfPlannedVisitsClosed'"
SELECT Count(*) As CountOfPlannedVisits, MAX(datename(month, dbo.tblVisit.DateConfirmed)) AS MonthName,
SUM(CASE WHEN tblVisit.VisitTypeRef <> '5' THEN 1 ELSE 0 END) AS CountOfPlannedVisitsClosed, CAST(100.0 * SUM("CountOfPlannedVisits") / SUM(CountOfPlannedVisitsClosed) AS Decimal(5,2) ) As OverallAttendance
FROM dbo.tblContract INNER JOIN
dbo.tblCustomer ON dbo.tblContract.CustomerRef = dbo.tblCustomer.CustomerID INNER JOIN
dbo.tblContractSite ON dbo.tblContract.ContractID = dbo.tblContractSite.ContractRef INNER JOIN
dbo.tblVisit ON dbo.tblContractSite.CardNumber = dbo.tblVisit.CardNumber
WHERE (tblCustomer.CustomerNumber = '08434')
AND (tblVisit.Routine = '1')
AND year(tblVisit.DateConfirmed) = Year('2013')--#DateYear)
AND dbo.IsOnHoldEx(tblContract.OnHold, tblContractSite.OnHold, tblContract.OnHoldStartDate, tblContract.OnHoldEndDate, tblContractSite.OnHoldStartDate, tblContractSite.OnHoldEndDate) = 0
AND tblVisit.Deleted = 0 -- make sure we dont pull through deleted visits
AND (tblContractSite.DateInactive is NULL or tblContractSite.DateInactive > GetDate())
GROUP BY month(dbo.tblVisit.DateConfirmed)
Any help would be greatly appreciated as I'm not really sure where to go from here!
Thanks
You can only reference a column alias (like CountOfPlannedVisits in your case) in the order by clause. Anywhere else you have to repeat the expression or use a subquery table, something like :
select CountOfPlannedVisits,
CountOfPlannedVisitsClosed,
100 * CountOfPlannedVisits / CountOfPlannedVisitsClosed, ...
from (
select some_expression as CountOfPlannedVisits ,
some_other_expression as CountOfPlannedVisitsClosed
....
) a_table
....

How do I use the value from row above when a given column value is zero?

I have a table of items by date (each row is a new date). I am drawing out a value from another column D. I need it to replace 0s though. I need the following logic: when D=0 for that date, use the value in column D from the date prior.
Actually, truth be told, I need it to say, when D is 0, use the value from the latest date where D was not a 0, but the first will get me most of the way there.
Is there a way to build this logic? Maybe a CTE?
Thank you very much.
PS I'm using SSMS 2008.
EDIT: I wasn't very clear at first. The value I want to change is not the date. I want change the value in D with the latest non-zero value from D, based on date.
May be the following query might help you. It uses the OUTER APPLY to fetch the results. Screenshot #1 shows the sample data and query output against the sample data. This query can be written better but this is what I could come up with right now.
Hope that helps.
SELECT ITM.Id
, COALESCE(DAT.New_D, ITM.D) AS D
, ITM.DateValue
FROM dbo.Items ITM
OUTER APPLY (
SELECT
TOP 1 D AS New_D
FROM dbo.Items DAT
WHERE DAT.DateValue < ITM.DateValue
AND DAT.D <> 0
AND ITM.D = 0
ORDER BY DAT.DateValue DESC
) DAT
Screenshot #1:
UPDATE t
Set value = SELECT value
FROM table
WHERE date = (SELECT MAX(t1.date)
FROM table t1
WHERE t1.value != 0
AND t1.date < t.date)
FROM table t
WHERE t.value = 0
You could maybe something like this as part of an update script...
SET myTable.D = (
SELECT TOP 1 myTable2.D
FROM myTable2
WHERE myTable2.myDateField < myTable.myDateField
AND myTable2.D != 0
ORDER BY myTable2.myDateField DESC)
That's assuming that you want to actually update the data though rather than just replace the values for the purpose of a select query.
How about:
SELECT
i.ID,
i.DateValue,
D = CASE WHEN I.D <> 0 THEN I.D ELSE X.D END
FROM
Items I
OUTER APPLY (
SELECT TOP 1 S.D
FROM Items S
WHERE S.DATEVALUE < I.DATEVALUE AND S.D <> 0
ORDER BY S.DATEVALUE DESC
) X
SELECT t.id,
CASE WHEN t.D = 0 THEN t0.D
ELSE t.D END
FROM table AS t
LEFT JOIN table AS t0
ON t0.time =
(
SELECT MAX(time) FROM t0
WHERE t0.time < t.time
AND t0.D != 0
)
or if you want to avoid aggregates entirely,
SELECT t.id,
CASE WHEN t.D = 0 THEN t0.D
ELSE t.D END
FROM table AS t
LEFT JOIN table AS t0
ON t0.time < t.time
LEFT JOIN table AS tx
ON tx.time > t0.time
WHERE t0.D != 0
AND tx.D != 0
AND tx.id IS NULL -- i.e. there isn't any

Replacing In clause with exists

HI Gurus,
I'm looking to replace an IN clause with exists, but despite reading other similar cases on here I've not been able to apply them to my dataset.
I am looking to add in a column to my main query which tells me if a fund is found within a separate list, and if it does then label it 'emergency' and if not then 'non-emergency'
The list is defined like so:
select
f.id
FROM _audit a
INNER JOIN _fund f ON a.article_id = f.id
WHERE a.entity_name = 'Fund'
AND a.Changes LIKE
'%finance_code2%OldValue>3%'
)
UNION
(
select
id AS fund_reference
FROM _fund
WHERE (finance_code2 LIKE '3%'
OR finance_code2 LIKE '9%')
AND finance_code2 IS NOT NULL
And so what I am looking for is essentially something like:
SELECT
...Main query here...
,CASE WHEN fund_id IN (list_details) THEN 'emergency' else 'non-emergency' end
I know that it would be more efficient to do something like
SELECT
...Main query here...
,SELECT CASE WHEN EXISTS
(SELECT fund_id FROM list_details WHERE fund_id IS NOT NULL) THEN 'emergency' else 'non-emergency' END
But every time I try it keeps returning false values (saying that funds are contained within the list when they are not)
In case it helps I'm using sql server 2005 and the main query is listed below, where the list_details result (id) is joined onto donation_fund_allocation on list_details.id = donation_fund_allocation.fund_id
As always any clue would be massively appreciated :)
Thanks!
Main query
SELECT
don.supporter_id AS contact_id
,don.id AS gift_id
,YEAR(don.date_received) AS calendar_year
,YEAR(don.date_received) - CASE WHEN MONTH(don.date_received) < 4 THEN 1 ELSE 0 END AS financial_year
,don.date_received AS date_received
,don.event_id AS event_id
,SUM(CASE WHEN don.gift_aid_status <> 4 THEN don.value_gross * ((dfa.percentage) / 100)
WHEN don.gift_aid_status = 4 AND don.value_net > don.value_gross
AND don.value_net <> 0 THEN don.value_net * ((dfa.percentage) / 100)
ELSE don.value_gross * ((dfa.percentage) / 100)
END
) AS donation_value
--**List details query to go in here**
FROM donation don WITH (nolock)
INNER JOIN donation_fund_allocation dfa WITH (nolock) ON dfa.donation_id = don.id
WHERE don.supporter_id IS NOT NULL
AND don.status = 4
AND don.value_gross <> 0
GROUP BY don.supporter_id
,don.id
,don.date_received
,don.event_id
You need to correlate the exists call with the outer query. As written you are just asking if there exist any rows in list_details where fund_id isn't null
So, what you actually want is
SELECT
...Main query here...
,SELECT CASE WHEN EXISTS
(SELECT 1 FROM list_details WHERE fund_id = outer.fund_id) THEN 'emergency' else 'non-emergency' END
Where outer is the table alias for where fund_id can be found in your main select
You could write a function which takes the fund_id and returns an appropriate string value of "emergency" or "non-emergency".