SQL Count syntax error - sql

I'm having trouble counting two things in an SQL query. I need to pull a list of files that have been accessed at least five times each by employees from both marketing and sales. I know I'm using COUNT incorrectly but I don't know how. Here's my query so far:
SELECT FileId
FROM Files
JOIN FileAccesses ON Files.FileId = FileAccesses.FileId
WHERE Count(AccessUserGroup=1)>5 AND Count(AccessUserGroup=2)>5
It produces the error
Incorrect syntax near ')'.
Files is a table with the int FileId as its primary key. FileAccesses stores FileId values from Files but not as the primary key. It keeps track of a bunch of metadata every time a user touches a file. For the purposes of this question, the part that matters is AccessUserGroup, a tinyint that is set to 1 for marketing and 2 for sales.

This is the query that you want:
SELECT fa.FileId
FROM FileAccesses fa
GROUP BY fa.FileId
HAVING SUM(CASE WHEN AccessUserGroup = 1 THEN 1 ELSE 0 END) > 5 AND
SUM(CASE WHEN AccessUserGroup = 2 THEN 1 ELSE 0 END) > 5;
Notes:
You don't need theJOIN, unless your FileAccesses could have a file id not in Files (which I consider unlikely).
You should be using GROUP BY if you want to use aggregation functions.
The comparisons go in the HAVING clause.
The COUNT() with an expression produces an error in SQL Server (it works in some other databases). SUM() with CASE does the conditional aggregation.

I guess you'll need the actual file info as well not just the file id, so you can use a cte (common table expression) https://msdn.microsoft.com/en-us/library/ms175972.aspx
;WITH cte AS (
SELECT FileAccesses.FileId FROM FileAccesses
GROUP BY FileId
HAVING COUNT(CASE AccessUserGroup WHEN 1 THEN 1 ELSE NULL END) > 5
AND COUNT(CASE AccessUserGroup WHEN 2 THEN 1 ELSE NULL END) > 5
)
SELECT * FROM File
INNER JOIN cte ON File.FileId = cte.FileId

Related

using correlated subquery in the case statement

I’m trying to use a correlated subquery in my sql code and I can't wrap my head around what I'm doing wrong. A brief description about the code and what I'm trying to do:
The code consists of a big query (ALIASED AS A) which result set looks like a list of customer IDs, offer IDs and response status name ("SOLD","SELLING","IRRELEVANT","NO ANSWER" etc.) of each customer to each offer. The customers IDs and the responses in the result set are non-unique, since more than one offer can be made to each customer, and a customer can have different response for different offers.
The goal is to generate a list of distinct customer IDs and to mark each ID with 0 or 1 flag :
if the ID has AT LEAST ONE offer with status name is "SOLD" or "SELLING" the flag should be 1 otherwise 0. Since each customer has an array of different responses, what I'm trying to do is to check if "SOLD" or "SELLING" appears in this array for each customer ID, using correlated subquery in the case statement and aliasing the big underlying query named A with A1 this time:
select distinct
A.customer_ID,
case when 'SOLD' in (select distinct A1.response from A as A1
where A.customer_ID = A1.customer_ID) OR
'SELLING' in (select distinct A1.response from A as A1
where A.customer_ID = A1.customer_ID)
then 1 else 0 end as FLAG
FROM
(select …) A
What I get is a mistake alert saying there is no such object as A or A1.
Thanks in advance for the help!
You can use exists with cte :
with cte as (
<query here>
)
select c.*,
(case when exists (select 1
from cte c1
where c1.customer_ID = c.customer_ID and
c1.response in ('sold', 'selling')
)
then 1 else 0
end) as flag
from cte c;
You can also do aggregation :
select customer_id,
max(case when a.response in ('sold', 'selling') then 1 else 0 end) as flag
from < query here > a;
group by customer_id;
With statement as suggested by Yogesh is a good option. If you have any performance issues with "WITH" statement. you can create a volatile table and use columns from volatile table in your select statement .
create voltaile table as (select response from where response in ('SOLD','SELLING').
SELECT from customer table < and join voltaile table>.
The only disadvantge here is volatile tables cannot be accessed after you disconnect from session.

How to get three different types of score for each text in one row in SQL Server?

I am a new SQL developer and I am trying to write a query that will retrieve the following results from three tables in the database:
ID Text Score #1 Score #2 Score #3
The schema for the three tables are:
T_TextScore Table: Id, TextId, Label, Score, TypeId
T_Text: Id, Text
T_Status Table: Id, Type
The T_TextScore table contains three different types of scroes for each text. I was able to retrieve the scores for all texts but I am still unable to show each type of score for each text in one row as illustrated above. So could you please tell me how I can get the desired result?
Here's the query I have and I think it is not efficient in terms of performance as well:
SELECT T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM T_TextScore INNER JOIN
T_Text ON T_TextScore.TextId = T_Text.Id
WHERE (T_TextScore.TypeId = 3)
UNION
SELECT T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM T_TextScore INNER JOIN
T_Text ON T_TextScore.TextId = T_Text.Id
WHERE (T_TextScore.TypeId = 4)
UNION
SELECT T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM T_TextScore INNER JOIN
T_Text ON T_TextScore.TextId = T_Text.Id
WHERE (T_TextScore.TypeId = 5);
UPDATE:
After using the query suggested by #Craig Young, I got two rows for each text and I don't know why. Could you please explain to me why?
enter image description here
You want conditional aggregation. My best guess is simply:
SELECT ts.TextId,
MAX(CASE WHEN ts.TypeId = 3 THEN ts.Score END) as Score_1,
MAX(CASE WHEN ts.TypeId = 4 THEN ts.Score END) as Score_2,
MAX(CASE WHEN ts.TypeId = 5 THEN ts.Score END) as Score_3
FROM T_TextScore ts
GROUP BY ts.TextId;
Your query isn't doing quite what you asked for. The following would be a much better way of doing what you're currently doing:
SELECT ts.TextId, t.Text, ts.Label, ts.Score
FROM T_TextScore ts /* Table alias makes query much more readable */
INNER JOIN T_Text t ON
ts.TextId = t.Id
WHERE ts.TypeId IN (3, 4, 5)
However, the first part of your question suggests you actually want to pivot your data?
If so you can use PIVOT syntax. Or manual pivoting:
SELECT ts.TextId,
/* Use aggregate function to get only 1 per TextId */
MAX(t.Text) AS Text, MAX((ts.Label) AS Label,
/* Simply move ts.Score to the correct column to be summed. */
SUM(CASE WHEN ts.TypeId = 3 THEN ts.Score ELSE 0 END) AS Score3,
SUM(CASE WHEN ts.TypeId = 4 THEN ts.Score ELSE 0 END) AS Score4,
SUM(CASE WHEN ts.TypeId = 5 THEN ts.Score ELSE 0 END) AS Score5
FROM T_TextScore ts
INNER JOIN T_Text t ON
ts.TextId = t.Id
WHERE ts.TypeId IN (3, 4, 5)
GROUP BY ts.TextId
NOTE: PIVOT syntax is a little more succinct. But strangely I have seen it run slightly slower than manual pivot on occasion. So if performance is important, you'll have to benchmark.
Based on your comment:
After using the query suggested by #Craig Young, I got two rows for each text and I don't know why.
You probably either removed the GROUP BY clause, or included Text and Label in the GROUP BY. This made me realise that I'd forgotten to deal with these 2 columns which weren't part of either aggregate or GROUP BY.
I've updated my query above appropriately. However, I should point out lack of sample data makes it tricky to determine exactly what you're trying to achieve - particularly with the Label column which could be different per Score Type.

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;

Counting multiple columns with the same criteria

I would like to count multiple columns based on their contents. Each column I want to count(alpha,bravo and charlie) has either 'T' or 'F', I only want to count those with a 'T'.
So far i have:
select t1.name, count(t1.alpha), count(t1.bravo), count(t1.charlie)
from t1
where alpha='T',
or bravo='t'
or charlie='t'
group by name
order by name asc;
However the figures i am getting dont match what I get if i run
select count(*)
from t1
where name='test'
and alpha='t';
this example would need to be run in 16 different configurations to get the results i am seeking hence it not being a practical solution to write multiple versions of it.
Remove the where clause and use Conditional Aggregate instead, which will help you to count the rows only when data is T.
select t1.name,
count(case when t1.alpha ='T' then 1 end),
count(case when t1.bravo='T' then 1 end),
count(case when t1.charlie='T' then 1 end)
from t1
group by name
order by name asc;

Prevent duplicate COUNT in SELECT query

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;