Count specific values that belong to the same column SQL Server - sql

I would like to ask for help I would like to count the number of records but having 2 conditions for 2 specific values only from a one single column:
Count the records first:
count(service) as contacts
service | state | address
service1| 1 |123
service2| 1 |321
service3| 3 |332
service1| 2 |333
service2| 2 |111
service3| 3 |333
1st result
Service | Contacts | status
service1| 1 | 1
service1| 1 | 2
service1| 1 | 3
service2| 1 | 1
service2| 1 | 2
service2| 1 | 3
if status = 1 and 2 then add to count else 0 (only count who's "status" is equal to 1 and 2.
Result:
Final result
Service | Contacts
Service1 | 2
Service2 | 2
sorry for the confusion
Thanks for your big help

This should work for all major databases
SELECT service,
SUM(CASE WHEN status IN(1,2) THEN contacts ELSE 0 END) as Contacts
FROM (your query) as x
GROUP BY service

select service,
sum(case when status in(1,2) then contacts else 0 end) as contact_count
from MyTable
group by service
What this will do is evaluate each row and include the contacts value in the sum where the status is one that is required. As it is an aggregate, you need to group by the service

Related

SQL for Microsoft Access 2013

I am trying to get a count of customers who return for additional services that takes into account a comparison of assessment scores prior to the service, grouped by the type of Service. (Ultimately, I also want to be able to ignore returns that are within a month of the orginal service, but I'm pretty sure I can get that wrinkle sorted myself)
When counting results for a particular service, it should look at returns to any service type, not just the original service type. (Edit: *It should also look at all future returns, not just the next or the most recent *).
It does not need to be run often, but there are 15000+ lines of data and computational resources are limited by an underpowered machine (this is for a nonprofit organization), so efficiency would be nice but not absolutely needed.
Sample Data
ServiceTable
CustomerID Service Date ScoreBefore
A Service1 1/1/2017 1
A Service2 1/3/2017 1
A Service1 1/1/2018 4
B Service3 3/1/2018 3
B Service1 6/1/2018 1
B Service1 6/2/2018 1
C Service2 1/1/2019 4
C Service2 6/1/2019 1
Results should be (not taking into account the date padding option):
Service1
ReturnedWorse 0
ReturnedSame 2
ReturnedBetter 1
Service2
ReturnedWorse 1
ReturnedSame 0
ReturnedBetter 1
Service3
ReturnedWorse 2
So far, I have tried creating make table queries that could then be queried to get the aggregate info, but I am a bit stuck and suspect there may be a better route.
What I have tried:
SELECT CustomerID, Service, Date, ScoreBefore INTO ReturnedWorse
FROM ServiceTable AS FirstStay
WHERE ((((SELECT COUNT(*)
FROM ServiceTable AS SecondStay
WHERE FirstStay.CustomerID=SecondStay.CustomerID
AND
FirstStay.ScoreBefore> SecondStay.ScoreBefore
AND
SecondStay.Date > FirstStay.Date))));
Any help would be greatly appreciated.
This would have been easier to do with window functions, but they are not available in ms-access.
Here is a query that solves my understanding of your question :
t0: pick a record in the table (a customer buying a service)
t1 : pull out the record corresponding to the next time the same customer contracted any service with an INNER JOIN and a correlated subquery (if there is no such record, the initial record is not taken into account)
compare the score of the previous record to the current one
group the results by service id
You can see it in action in this db fiddlde. The results are slightly different from your expectation (see my comments)... but they are consistent with the above explanation ; you might want to adapt some of the rules to match your exact expected result, using the same principles.
SELECT
t0.service,
SUM(CASE WHEN t1.scorebefore < t0.scorebefore THEN 1 ELSE 0 END) AS ReturnedWorse,
SUM(CASE WHEN t1.scorebefore = t0.scorebefore THEN 1 ELSE 0 END) AS ReturnedSame,
SUM(CASE WHEN t1.scorebefore > t0.scorebefore THEN 1 ELSE 0 END) AS ReturnedBetter
FROM
mytable t0
INNER JOIN mytable t1
ON t0.customerid = t1.customerid
AND t0.date < t1.date
AND NOT EXISTS (
SELECT 1
from mytable
WHERE
customerid = t1.customerid
AND date < t1.date
AND date > t0.date
)
GROUP BY t0.service
| service | ReturnedWorse | ReturnedSame | ReturnedBetter |
| -------- | ------------- | ------------ | -------------- |
| Service1 | 0 | 2 | 0 |
| Service2 | 1 | 0 | 1 |
| Service3 | 1 | 0 | 0 |
From your comments, I understand that you want to take into account all future returns and not only the next one. This eliminates the need for a correlatead subquery, and actually yields your expected output. See this db fiddle :
SELECT
t0.service,
SUM(CASE WHEN t1.scorebefore < t0.scorebefore THEN 1 ELSE 0 END) AS ReturnedWorse,
SUM(CASE WHEN t1.scorebefore = t0.scorebefore THEN 1 ELSE 0 END) AS ReturnedSame,
SUM(CASE WHEN t1.scorebefore > t0.scorebefore THEN 1 ELSE 0 END) AS ReturnedBetter
FROM
mytable t0
INNER JOIN mytable t1
ON t0.customerid = t1.customerid
-- AND t0.service = t1.service
AND t0.date < t1.date
GROUP BY t0.service
| service | ReturnedWorse | ReturnedSame | ReturnedBetter |
| -------- | ------------- | ------------ | -------------- |
| Service1 | 0 | 2 | 1 |
| Service2 | 1 | 0 | 1 |
| Service3 | 2 | 0 | 0 |

SQL query that returns a set

I have a following table
socket_id | event_type | response
----------+------------+---------
2 | 2 | 404
----------+------------+---------
2 | 4 | 200
----------+------------+---------
2 | 2 | 500
----------+------------+---------
3 | 2 | 400
----------+------------+---------
2 | 3 | 404
----------+------------+---------
I need an SQL query that returns a set of all sockets with number of different event types registered by each of them, ordered by socket_id ASC
So far I wrote this query by it doesn't return different event_types for same socket_id.
SELECT socket_id,
SUM(CASE WHEN event_type >= 1 THEN 1 ELSE 0 END) types
FROM Events
GROUP BY socket_id;
As if you mean this :
SELECT socket_id,
count(distinct event_type) as types
FROM Events
GROUP BY socket_id
ORDER BY socket_id;
Your description suggests me :
SELECT socket_id, COUNT(event_type) AS types
FROM Events
GROUP BY socket_id
ORDER BY socket_id;
If you want unique event_type count then use DISTINCT inside COUNT().

Postgres: How to use value of field in subquery

I have db table like this:
id | user | service | pay_date | status
---+------+---------+--------------+------------
1 | john | 1 | 2014-08-20 | 1
2 | john | 3 | 2014-07-24 | 0
I am trying to build query which return all users who are not payed service 1 or service 3 between 2 dates and haven`t active services (status = 0 for service 1 and service 2). The problem is that query return service 3, but this is not correct because service 1 is active.
SELECT *
FROM services
WHERE date > 2014-07-01
AND date < 2014-07-30
AND service = 1 OR service = 3
My solution is to check if service 3 is active when i`am checking service 1. Something like that...
SELECT *
FROM services
WHERE date > 2014-07-01
AND date < 2014-07-30
AND ((service = 1 AND "SERVICE 3 IS NOT ACTIVE") OR service = 3)
but i cant check if "SERVICE 3 IS NOT ACTIVE" for current user
I think you want NOT EXISTS, i.e. checking that another active record does not exist for the same user, possibly something like:
SELECT *
FROM Services
WHERE Service IN (1, 3)
AND pay_date > '2014-07-01'
AND pay_date < '2014-07-30'
AND NOT EXISTS
( SELECT 1
FROM services AS s2
WHERE s2.Name = s.Name
AND s2.Status = 1
AND s2.Service IN (1, 3)
AND s2.pay_date > '2014-07-01'
AND s2.pay_date < '2014-07-30'
);
There is no single record that meets all those criteria.
id 1 record cannot be both service = 1 & service = 3 at the some time
id | user | service | pay_date | status
---+------+---------+--------------+------------
1 | john | 1 | 2014-08-20 | 1
id 2 record cannot be both service = 1 & service = 3 at the some time
id | user | service | pay_date | status
---+------+---------+--------------+------------
2 | john | 3 | 2014-08-24 | 0 -- nb: I changed this date!
SO: your criteria require comparing record 1 to record 2 (or more broadly, that you have to compare "across records"). below I use EXISTS to compare across records.
SELECT
*
FROM services
WHERE (pay_date >= '2014-08-01'
AND pay_date < '2014-08-30')
AND service = 1
AND EXISTS (
SELECT
NULL
FROM services s3
WHERE (s3.pay_date >= '2014-08-01'
AND s3.pay_date < '2014-08-30')
AND s3.service = 3
and s3.status = 1
AND s3.user = services.user
)
see SQLfiddle Demo
by the way, it is more conventional to use a date range via >= & <

join multiple row in table by filed value

i have a table company row like this :
id(int) |name(string) |maincategory(int) |subcategory(string)
1 |Google |1 |1,2,3
2 |yahoo |4 |4,1
and other table category like:
id(int) |name(string)
1 |Search
2 |Email
3 |Image
4 |Video
i want to join tow table by company.subcategory = category.id
is it possible in sql ?
Start by splitting your subcategory column. In the end you should have an additional company_category table with company_id and category_id as columns.
company_id(int) |category_id(int)
1 |1
1 |2
1 |3
2 |4
2 |1
Your design is invalid. You shoud have another table called companySubcategories or something like that.
This table shoud have two columns companyId an categoryId.
Then your select would look like this:
select <desired fields> from
company c
join companySubcategories cs on cs.companyId = cs.id
join category ct on ct.id = cs.categoryId
you can do like below...
select * from
company c, category cc
where c. subcategory like '%'||cc.id||'%';
it is working as expected in oracle database ..
You could introduce a new table company_subcategory to keep track of subcategories
id (int) | subcategory(int)
1 | 1
1 | 2
1 | 3
2 | 1
2 | 4
then you would be able to run select as
select company.name AS company, category.name AS category
FROM company
JOIN company_subcategory
ON company.id = company_subcategory.company
JOIN category
ON company_subcategory.subcategory = category.id;
to get
+---------+----------+
| company | category |
+---------+----------+
| google | search |
| google | email |
| google | image |
| yahoo | search |
| yahoo | video |
+---------+----------+
SELECT *
FROM COMPANY CMP, CATEGORY CT
WHERE (SELECT CASE
WHEN INSTR(CMP.SUB_CATEGORY, CT.ID) > 0 THEN
'TRUE'
ELSE
'FALSE'
END
FROM DUAL) = 'TRUE'
This query looks for the ID in the SUB_CATEGORY, using the INSTR function.
In case it does exist, the row is returned.
The output is as below
ID NAME MAIN_CATEGORY SUB_CATEGORY ID NAME
1 Google 1 1,2,3 1 Search
1 Google 1 1,2,3 2 Email
1 Google 1 1,2,3 3 Image
2 yahoo 2 4,1 1 Search
2 yahoo 2 4,1 4 Video
Hope it helps.
However, I suggest you avoid this type of entries, as an ID should have separate entries and not combined entries. This may create problems in future, so it would be better to avoid it now.

Problem with advanced distinct SQL query

Ok this one is realy tricky :D
i have a this table
bills_products:
- bill_id - product_id - action -
| 1 | 4 | add |
| 1 | 5 | add |
| 2 | 4 | remove |
| 2 | 1 | add |
| 3 | 4 | add |
as you can see product with the id 4 was added at bill 1 then removed in bill 2 and added again in bill 3
All Bills belong to a bill_group. But for the simplicity sake let's assume all the bills are in the same group.
Now i need a SQL Query that shows all the products that are currently added at this group.
In this example that would be 5, 1 and 4. If we would remove the bill with id 3 that would be 5 and 1
I've tried to do this with DISTINCT but it's not powerful enough or maybe I'm doing it wrong.
This seems to work in SQL Server at least:
select product_id
from (
select product_id,
sum((case when action='add' then 1 else -1 end)) as number
from bills_products
group by product_id
) as counts
where number > 0
SELECT DISTINCT product_id FROM bills_products WHERE action = 'add';
GSto almost had it, but you have to ORDER BY bill_id DESC to ensure you get the latest records.
SELECT DISTINCT product_id FROM bills_products
WHERE action = 'add'
ORDER BY bill_id DESC;
(P.S. I think most people would say it's a best practice to have a timestamp column on tables like this where you need to be able to know what the "newest" row is. You can't always rely on ids only ascending.)