SQL: Using COUNT(*) Instead of EXISTS - sql

Is it possible to use COUNT in place of EXISTS?
I have following query:
SELECT *
FROM Goals G
WHERE EXISTS (SELECT NULL FROM tfv_home_last6(G.Date, G.Home) WHERE GameNumber <= 6 AND
HomeGoals >= 3)
Instead of returning the row if at least one row exists in the subquery, I'd like to specify a number of rows that need to be returned in the subquery, something like
SELECT *
FROM Goals G
WHERE ROWCOUNT(*) >= 2 (SELECT NULL FROM tfv_home_last6(G.Date, G.Home) WHERE GameNumber <= 6 AND
HomeGoals >= 3)
I'm not sure how to go about it?
I'm using SQL Server 2012.

You can do the subquery pretty much just like you describe:
SELECT *
FROM Goals G
WHERE (SELECT count(*)
FROM tfv_home_last6(G.Date, G.Home)
WHERE GameNumber <= 6 AND HomeGoals >= 3
) > 0;
However, this requires calculating the entire count. The exists form is more efficient, because it stops at the first matching record.
In SQL Server 2012, you could also use `cross apply:
SELECT *
FROM Goals G cross apply
(select count(*) as cnt
FROM tfv_home_last6(G.Date, G.Home)
WHERE GameNumber <= 6 AND HomeGoals >= 3
) a
WHERE a.cnt > 0;
I do not know which would have better performance, the correlated subquery in the where clause or the
cross apply version.

Related

SQL Except or Minus

I have two table with same "id" for companies and i need for "select all" make as minus special companies which do invoice in concrete date.
I tried minus fuctions but its does not work in our ERP systems but i can use "except" but it does not work the way I need it.
SELECT ad.idfirmy FROM aadresar as ad .............\\ (select all)
EXCEPT
SELECT DISTINCT f.idfirmy FROM ddoklfak as f ...... \\ (this i need minus from all sellect)
WHERE modul = 'FAV' and f.datvyst >= '<<datum_od>>' and f.datvyst <=
'<<datum_do>>') db on db.idfirmy = ad.idfirmy
For examble i have "idfirmy value 193451" which is in second select and after except is still this value in result but its so bad.
I need second select subtract from first
I suggest either you delete the db part
SELECT ad.idfirmy
FROM aadresar AS ad
EXCEPT
SELECT DISTINCT f.idfirmy
FROM ddoklfak AS f
WHERE modul = 'FAV' AND f.datvyst >= '<<datum_od>>'
AND f.datvyst <= '<<datum_do>>';
Or use NOT EXISTS instead
SELECT ad.idfirmy
FROM aadresar AS ad
WHERE NOT EXISTS
(
SELECT 1
FROM ddoklfak AS f
WHERE f.idfirmy = ad.idfirmy
AND modul = 'FAV'
AND f.datvyst >= '<<datum_od>>'
AND f.datvyst <= '<<datum_do>>'
);

Looking for duplicates based on a few other columns

I am trying to find the rows where PilotID has used the shimpmentNumber more than once.
I have this so far.
select f_Shipment_ID
,f_date
,f_Pilot_ID
,f_Shipname
,f_SailedFrom
,f_SailedTo
,f_d_m
,f_Shipmentnumber
,f_NumberOfPilots
from t_shipment
where f_Pilot_ID < 10000
and f_NumberOfPilots=1
and f_Shipmentnumber in(select f_Shipmentnumber
from t_shipment
group by f_Shipmentnumber
Having count(*) >1)
Try something like this:
-- The CTE determines the f_Pilot_ID/f_Shipmentnumber combinations that appear more than once.
with DuplicateShipmentNumberCTE as
(
select
f_Pilot_ID,
f_Shipmentnumber
from
t_shipment
where
f_Pilot_ID < 10000 and
f_NumberOfPilots = 1
group by
f_Pilot_ID,
f_Shipmentnumber
having
count(1) > 1
)
select
Shipment.f_Shipment_ID,
Shipment.f_date,
Shipment.f_Pilot_ID,
Shipment.f_Shipname,
Shipment.f_SailedFrom,
Shipment.f_SailedTo,
Shipment.f_d_m,
Shipment.f_Shipmentnumber,
Shipment.f_NumberOfPilots
from
-- The join is used to restrict the result set to the shipments identified by the CTE.
t_shipment Shipment
inner join DuplicateShipmentNumberCTE CTE on
Shipment.f_Pilot_ID = CTE.f_Pilot_ID and
Shipment.f_Shipmentnumber = CTE.f_Shipmentnumber
where
f_NumberOfPilots = 1;
You can also do this with a subquery if you want to—or if you're using an old version of SQL Server that doesn't support CTEs—but I find the CTE syntax to be more natural, if only because it enables you to read and understand the query from the top down, rather than from the inside out.
In your sub select use:
select f_Shipmentnumber
from t_shipment
group by f_pilot_id, f_Shipmentnumber
Having count(*) >1
How about this
select f_Shipment_ID
,f_date
,f_Pilot_ID
,f_Shipname
,f_SailedFrom
,f_SailedTo
,f_d_m
,f_Shipmentnumber
,f_NumberOfPilots
from t_shipment
where f_Pilot_ID < 10000
and f_NumberOfPilots=1
and f_Pilot_ID IN (select f_Pilot_ID
from t_shipment
group by f_Pilot_ID, f_Shipmentnumber
Having count(*) >1)

SQL Server check if where clause is true for any row

I'm going to select those provinces which intersects any railroad. So I do it like this (Using SQL Spatial):
SELECT * FROM ProvinceTable
WHERE (
SELECT count(*)
FROM RailroadTable
WHERE ProvinceTable.Shape.STIntersects(RailroadTable.Shape) > 1
) > 0
But it is not efficient because it has to check the intersection between every single railroad geometry and province geometry in order to calculate the count. However it is better to stop the where clause as soon as every first intersection detected and there is no need to check others. Here is what I mean:
SELECT * FROM ProvinceTable
WHERE (
--return true if this is true for any row in the RailroadTable:
-- "ProvinceTable.Shape.STIntersects(RailroadTable.Shape) > 1"
)
So is there a better way to rewrite this query for such a goal?
EDIT
Surprisingly This query takes the same time and returns no row:
SELECT * FROM ProvinceTable
WHERE EXISTS (
SELECT *
FROM RailroadTable
WHERE ProvinceTable.Shape.STIntersects(RailroadTable.Shape) > 1
)
You want to use exists:
SELECT pt.*
FROM ProvinceTable pt
WHERE EXISTS (SELECT 1
FROM RailroadTable rt
WHERE pt.Shape.STIntersects(rt.Shape) = 1
);

troubles with next and previous query

I have a list and the returned table looks like this. I took the preview of only one car but there are many more.
What I need to do now is check that the current KM value is larger then the previous and smaller then the next. If this is not the case I need to make a field called Trustworthy and should fill it with either 1 or 0 (true/ false).
The result that I have so far is this:
validKMstand and validkmstand2 are how I calculate it. It did not work in one list so that is why I separated it.
In both of my tries my code does not work.
Here is the code that I have so far.
FullList as (
SELECT
*
FROM
eMK_Mileage as Mileage
)
, ValidChecked1 as (
SELECT
UL1.*,
CASE WHEN EXISTS(
SELECT TOP(1)UL2.*
FROM FullList AS UL2
WHERE
UL2.FK_CarID = UL1.FK_CarID AND
UL1.KM_Date > UL2.KM_Date AND
UL1.KM > UL2.KM
ORDER BY UL2.KM_Date DESC
)
THEN 1
ELSE 0
END AS validkmstand
FROM FullList as UL1
)
, ValidChecked2 as (
SELECT
List1.*,
(CASE WHEN List1.KM > ulprev.KM
THEN 1
ELSE 0
END
) AS validkmstand2
FROM ValidChecked1 as List1 outer apply
(SELECT TOP(1)UL3.*
FROM ValidChecked1 AS UL3
WHERE
UL3.FK_CarID = List1.FK_CarID AND
UL3.KM_Date <= List1.KM_Date AND
List1.KM > UL3.KM
ORDER BY UL3.KM_Date DESC) ulprev
)
SELECT * FROM ValidChecked2 order by FK_CarID, KM_Date
Maybe something like this is what you are looking for?
;with data as
(
select *, rn = row_number() over (partition by fk_carid order by km_date)
from eMK_Mileage
)
select
d.FK_CarID, d.KM, d.KM_Date,
valid =
case
when (d.KM > d_prev.KM /* or d_prev.KM is null */)
and (d.KM < d_next.KM /* or d_next.KM is null */)
then 1 else 0
end
from data d
left join data d_prev on d.FK_CarID = d_prev.FK_CarID and d_prev.rn = d.rn - 1
left join data d_next on d.FK_CarID = d_next.FK_CarID and d_next.rn = d.rn + 1
order by d.FK_CarID, d.KM_Date
With SQL Server versions 2012+ you could have used the lag() and lead() analytical functions to access the previous/next rows, but in versions before you can accomplish the same thing by numbering rows within partitions of the set. There are other ways too, like using correlated subqueries.
I left a couple of conditions commented out that deal with the first and last rows for every car - maybe those should be considered valid is they fulfill only one part of the comparison (since the previous/next rows are null)?

Why colums in SELECT not belongs to SELECT

I have this select, but does not work.
select
a.code1,
a.data1,
a.stval,
(select sum(col1+col2+col3) from tad ) as sum1,
(select sum(col7+col8+col9) from tbac) as sum2,
CASE
WHEN (sum1+sum2) > 100 THEN (a.stval * sum1)
WHEN (sum1+sum2( <= 100 THEN (a.stval * sum2)
END as newdat1
from arti as a
Where is the error? why (sum1+sum2) its error?
Thanks
(sum1 + sum2) is an error because these identifiers are not defined in the scope where you are trying to use them. In an SQL select list, you cannot use symbols declared in the same select list, irrespective of their position on the list. Use a subquery if you need to access sum1 and sum2.
The specific reason is that SQL is a descriptive language that does not guarantee the order of evaluation of expressions. This is true in the select clause. This is true in the where clause. It is true in the from clause. SQL describes what the results look like. It does not prescribe the specific actions.
As a result, SQL does not allow identifiers defined in the select to be used in the same select clause (nor in the where clause at the same level). The expressions can be processed in any order.
The normal solution in your case is to use a subquery or a CTE. In your case, though, the subqueries are independent of the outer query (as written), so I would move them to the from clause:
select a.code1, a.data1, a.stval, x1.sum1, x2.sum2,
(CASE WHEN x1.sum1 + x2.sum2 > 100 THEN a.stval * x1.sum1
WHEN x1.sum1 + x2.sum2 <= 100 THEN a.stval * x2.sum2
END) as newdat1
from arti a cross join
(select sum(col1+col2+col3) as sum1 from tad ) x1 cross join
(select sum(col7+col8+col9) as sum2 from tbac) x2;
EDIT:
You can use a subquery or CTE. But there is an approach that builds on the above:
select a.code1, a.data1, a.stval, x1.sum1, x2.sum2,
(CASE WHEN x1.sum1 + x2.sum2 > 100 THEN a.stval * x1.sum1
WHEN x1.sum1 + x2.sum2 <= 100 THEN a.stval * x2.sum2
END) as newdat1
from arti a join
(select ascon, sum(col1+col2+col3) as sum1
from tad
group by ascon
) x1
on x1.ascon = arti.code1 cross join
(select sum(col7+col8+col9) as sum2 from tbac) x2;