Prevent duplicate COUNT in SELECT query - sql

I have the following query which as you can see does multiple Count(CompetitorID) calls. Is this a performance issue, or does SQL Server 2008 'cache' the Count? If this is a performance issue, is it possible to store the Count to prevent the multiple lookups?
SELECT EventID,Count(CompetitorID) AS NumberRunners,
CASE WHEN Count(CompetitorID)<5 THEN 1
WHEN Count(CompetitorID)>=5 AND Count(CompetitorID)<=7 THEN 2
ELSE 3 END AS NumberPlacings
FROM Comps
GROUP BY EventID Order By EventID;

Its always a better practice to get the value once and use it subsequently whenever possible. In your case, you can always use Inner query to get the count only once and compute other (derived) columns off its value as shown below:
SELECT EventID, NumberRunners,
CASE WHEN NumberRunners <5 THEN 1
WHEN NumberRunners >=5 AND NumberRunners <=7 THEN 2
ELSE 3
END AS NumberPlacings
FROM (
SELECT EventID,
NumberRunners = Count(CompetitorID)
FROM Comps
GROUP BY EventID
) t
Order By EventID;

simplest would be this:
SELECT EventID,Count(distinct CompetitorID) AS NumberRunners,
CASE WHEN Count(distinct CompetitorID)<5 THEN 1
WHEN Count(distinct CompetitorID)>=5 AND Count(distinct CompetitorID)<=7 THEN 2
ELSE 3 END AS NumberPlacings
FROM Comps
GROUP BY EventID Order By EventID;

Related

Getting 1's and 0's instead of COUNT in this query

In my dataset, I'm trying to find how many Stores have had at least 10 events where the value of each event was greater than $100.
SELECT COUNT(EventId) >=10
FROM Event
WHERE Event.EventValue >100
GROUP BY Site.SiteName
I appear to just be getting 1's and 0's instead of getting the site names that fit this criteria
Should work in your case.
SELECT CASE WHEN COUNT(*) >= 10 THEN 1 ELSE 0 END
FROM (
SELECT COUNT(EventId) AS T
FROM Event
WHERE Event.EventValue >100
GROUP BY Site.SiteName
) DATA
Try this:
SELECT
COUNT(1)
FROM
(
SELECT
eventid
FROM
events
WHERE
eventvalue > 100
GROUP BY
eventid
HAVING
COUNT(1) >= 10
)
Your query is returning 0 and 1 to mean FALSE and TRUE, because your SELECT clause is asking whether the COUNT(EventId) is greater than 10. Instead, try selecting the SiteId and putting your COUNT > 10 criterion in a HAVING clause.
Try this query.
select Site.SiteId, Site.SiteName
from (select SiteId, count(distinct EventId)
from Event
where EventValue > 100
group by SiteId
having count(distinct EventId) >= 10
) EV
join Site
on EV.SiteId = Site.SiteId
;
1/0 are the answer as it is showing True/False as your SELECT cause is asking for the count greater than 10 not the name. It should work if you ask for the SiteID and look at count >10 as it will show names not a true false answer.

SQL Server iterating through time series data

I am using SQL Server and wondering if it is possible to iterate through time series data until specific condition is met and based on that label my data in other table?
For example, let's say I have a table like this:
Id Date Some_kind_of_event
+--+----------+------------------
1 |2018-01-01|dsdf...
1 |2018-01-06|sdfs...
1 |2018-01-29|fsdfs...
2 |2018-05-10|sdfs...
2 |2018-05-11|fgdf...
2 |2018-05-12|asda...
3 |2018-02-15|sgsd...
3 |2018-02-16|rgw...
3 |2018-02-17|sgs...
3 |2018-02-28|sgs...
What I want to get, is to calculate for each key the difference between two adjacent events and find out if there exists difference > 10 days between these two adjacent events. In case yes, I want to stop iterating for that specific key and put label 'inactive', otherwise 'active' in my other table. After we finish with one key, we start with another.
So for example id = 1 would get label 'inactive' because there exists two dates which have difference bigger that 10 days. The final result would be like that:
Id Label
+--+----------+
1 |inactive
2 |active
3 |inactive
Any ideas how to do that? Is it possible to do it with SQL?
When working with a DBMS you need to get away from the idea of thinking iteratively. Instead you need to try and think in sets. "Instead of thinking about what you want to do to a row, think about what you want to do to a column."
If I understand correctly, is this what you're after?
CREATE TABLE SomeEvent (ID int, EventDate date, EventName varchar(10));
INSERT INTO SomeEvent
VALUES (1,'20180101','dsdf...'),
(1,'20180106','sdfs...'),
(1,'20180129','fsdfs..'),
(2,'20180510','sdfs...'),
(2,'20180511','fgdf...'),
(2,'20180512','asda...'),
(3,'20180215','sgsd...'),
(3,'20180216','rgw....'),
(3,'20180217','sgs....'),
(3,'20180228','sgs....');
GO
WITH Gaps AS(
SELECT *,
DATEDIFF(DAY,LAG(EventDate) OVER (PARTITION BY ID ORDER BY EventDate),EventDate) AS EventGap
FROM SomeEvent)
SELECT ID,
CASE WHEN MAX(EventGap) > 10 THEN 'inactive' ELSE 'active' END AS Label
FROM Gaps
GROUP BY ID
ORDER BY ID;
GO
DROP TABLE SomeEvent;
GO
This assumes you are using SQL Server 2012+, as it uses the LAG function, and SQL Server 2008 has less than 12 months of any kind of support.
Try this. Note, replace #MyTable with your actual table.
WITH Diffs AS (
SELECT
Id
,DATEDIFF(DAY,[Date],LEAD([Date],1,0) OVER (ORDER BY [Id], [Date])) Diff
FROM #MyTable)
SELECT
Id
,CASE WHEN MAX(Diff) > 10 THEN 'Inactive' ELSE 'Active' END
FROM Diffs
GROUP BY Id
Just to share another approach (without a CTE).
SELECT
ID
, CASE WHEN SUM(TotalDays) = (MAX(CNT) - 1) THEN 'Active' ELSE 'Inactive' END Label
FROM (
SELECT
ID
, EventDate
, CASE WHEN DATEDIFF(DAY, EventDate, LEAD(EventDate) OVER(PARTITION BY ID ORDER BY EventDate)) < 10 THEN 1 ELSE 0 END TotalDays
, COUNT(ID) OVER(PARTITION BY ID) CNT
FROM EventsTable
) D
GROUP BY ID
The method is counting how many records each ID has, and getting the TotalDays by date differences (in days) between the current the next date, if the difference is less than 10 days, then give me 1, else give me 0.
Then compare, if the total days equal the number of records that each ID has (minus one) would print Active, else Inactive.
This is just another approach that doesn't use CTE.

SQLite3 database conditional summing

I am looking to order a list of keys based on the number of orders placed from a database containing order requests. Basically, on table, call it orders(o_partkey, o_returnflag) I am trying to get the total number of returns for each order. I have tried many variations of the following snippet with the goal schema returnlist(partkey, numreturns):
select O.o_partkey as partkey,
count(case when O.o_returnflag = 'R' then 1 else 0 end) as numreturns
from orders O
orderby quantity_returned desc;
I am very new to SQLite and am just jumping into the basics. This is an adjustment of a homework question (the actual question is more complex) but I have simplified down the issue I am having.
Consider using a derived table subquery with SUM() as the aggregate function:
SELECT dT.partkey, dT.numreturns
FROM
(SELECT O.o_partkey as partkey,
SUM(CASE WHEN O.o_returnflag = 'R' THEN 1 ELSE 0 END) as numreturns
FROM [ORDER] O
GROUP BY O.o_partkey) AS dT
ORDER BY dT.numreturns DESC;
Be sure to bracket name of table as [ORDER] is an SQLite key word.
Your problem is that COUNT counts rows, so it counts both 0 and 1 values.
You are not interested in any other rows, so you can just filter out the returns with WHERE:
SELECT o_partkey AS partkey,
COUNT(*) AS numreturns
FROM orders
WHERE o_returnflag = 'R'
ORDER BY 2 DESC;

filtering rows by checking a condition for group in one statement only

I have the following statement:
SELECT
(CONVERT(VARCHAR(10), f1, 120)) AS ff1,
CONVERT(VARCHAR(10), f2, 103) AS ff2,
...,
Bonus,
Malus,
ClientID,
FROM
my_table
WHERE
<my_conditions>
ORDER BY
f1 ASC
This select returns several rows for each ClientID. I have to filter out all the rows with the Clients that don't have any row with non-empty Bonus or Malus.
How can I do it by changing this select by one statement only and without duplicating all this select?
I could store the result in a #temp_table, then group the data and use the result of the grouping to filter the temp table. - BUT I should do it by one statement only.
I could perform this select twice - one time grouping it and then I can filter the rows based on grouping result. BUT I don't want to select it twice.
May be CTE (Common Table Expressions) could be useful here to perform the select one time only and to be able to use the result for grouping and then for selecting the desired result based on the grouping result.
Any more elegant solution for this problem?
Thank you in advance!
Just to clarify what the SQL should do I add an example:
ClientID Bonus Malus
1 1
1
1 1
2
2
3 4
3 5
3 1
So in this case I don't want the ClientID=2 rows to appear (they are not interesting). The result should be:
ClientID Bonus Malus
1 1
1
1 1
3 4
3 5
3 1
SELECT Bonus,
Malus,
ClientID
FROM my_table
WHERE ClientID not in
(
select ClientID
from my_table
group by ClientID
having count(Bonus) = 0 and count(Malus) = 0
)
A CTE will work fine, but in effect its contents will be executed twice because they are being cloned into all the places where the CTE is being used. This can be a net performance win or loss compared to using a temp table. If the query is very expensive it might come out as a loss. If it is cheap or if many rows are being returned the temp table will lose the comparison.
Which solution is better? Look at the execution plans and measure the performance.
The CTE is the easier, more maintainable are less redundant alternative.
You haven't specified what are data types of Bonus and Malus columns. So if they're integer (or can be converted to integer), then the query below should be helpful. It calculates sum of both columns for each ClientID. These sums are the same for each detail line of the same client so we can use them in WHERE condition. Statement SUM() OVER() is called "windowed function" and can't be used in WHERE clause so I had to wrap your select-list with a parent one just because of syntax.
SELECT *
FROM (
SELECT
CONVERT(VARCHAR(10), f1, 120) AS ff1,
CONVERT(VARCHAR(10), f2, 103) AS ff2,
...,
Bonus,
Malus,
ClientID,
SUM(Bonus) OVER (PARTITION BY ClientID) AS ClientBonusTotal,
SUM(Malus) OVER (PARTITION BY ClientID) AS ClientMalusTotal
FROM
my_table
WHERE
<my_conditions>
) a
WHERE ISNULL(a.ClientBonusTotal, 0) <> 0 OR ISNULL(a.ClientMalusTotal, 0) <> 0
ORDER BY f1 ASC

MySQL find count query

I have table called stats. In am inserting yes or no in the table, and I want to show the number of yes count and the number of no count.
Can somebody please help me with the query?
select yn, count(*)
from stats
group by yn;
Try something like this
SELECT SUM(CASE WHEN recommend = 'Y' THEN 1 ELSE 0 END) YesCount,
SUM(CASE WHEN recommend = 'N' THEN 1 ELSE 0 END) NoCount,
COUNT(*) TotalCount
FROM Stats
This is exactly what the GROUP BY clause and aggregate functions are for in SQL. The following should be what you need and more efficient then a CASE statement. It returns a table with two columns: recommend and no (which is the count of identical values in the recommend column. If what you said above is true, then this should return at most two rows.
SELECT recommend, count(*) AS no FROM stats GROUP BY recommend