SQL Server : combining COUNT(*) for different tables at once - sql

I have a few queries that I would like to combine into ONE query in order to not have to call out to the server multiple times.
An example of the queries I am using:
SELECT COUNT(*) AS mailCount1
FROM [WebContact].[dbo].[memberEmails]
WHERE contactdatetime > '01/01/06'
AND contactdatetime < '02/01/06'
SELECT COUNT(*) AS mailCount2
FROM [WebContact].[dbo].[otherEmails]
WHERE contactdatetime > '01/01/06'
AND contactdatetime < '02/01/06'
SELECT COUNT(*) AS mailCount3
FROM [WebContact].[dbo].[memberEmails]
WHERE contactdatetime > '02/01/06'
AND contactdatetime < '03/01/06'
SELECT COUNT(*) AS mailCount4
FROM [WebContact].[dbo].[otherEmails]
WHERE contactdatetime > '02/01/06'
AND contactdatetime < '03/01/06'
etc etc...
So as the examples above, only thing that changes are:
The FROM (memberEmails & otherEmails)
The > & < months (01/01/06, 02/01/06 | 02/01/06, 03/01/06 | etc...)
Is this possible to do with a single query?

First, use group by and just use two queries:
select year(contactdatetime) as yyyy, month(contactdatetime) as mm, count(*)
from WebContact].[dbo].[memberEmails]
group by year(contactdatetime), month(contactdatetime);
and:
select year(contactdatetime) as yyyy, month(contactdatetime) as mm, count(*)
from WebContact].[dbo].[otherEmails]
group by year(contactdatetime), month(contactdatetime);
Then, if you like, you can combine these into a single query:
select coalesce(me.yyyy, oe.yyyy) as yyyy, coalesce(me.mm, oe.mm) as mm,
coalesce(me.cnt, 0) as memberemailcnt,
coalesce(oe.cnt, 0) as otheremailcnt
from (select year(contactdatetime) as yyyy, month(contactdatetime) as mm, count(*) as cnt
from WebContact].[dbo].[memberEmails]
group by year(contactdatetime), month(contactdatetime)
) me full outer join
(select year(contactdatetime) as yyyy, month(contactdatetime) as mm, count(*) as cnt
from WebContact].[dbo].[otherEmails]
group by year(contactdatetime), month(contactdatetime)
) oe
on me.yyyy = oe.yyyy and me.mm = oe.mm;
A full outer join is not necessary if both tables have data for all months.

declare #emailCount table(tablename varchar(20), year int, month int, qty int)
insert into #emailCount
select 'memberEmails', year(contactdatetime), month(contactdatetime), count(*)
from [WebContact].[dbo].[memberEmails]
group by year(contactdatetime), month(contactdatetime)
insert into #emailCount
select 'otherEmails',year(contactdatetime), month(contactdatetime), count(*)
from [WebContact].[dbo].[otherEmails]
group by year(contactdatetime), month(contactdatetime)
select tablename, year, month, qty from #emailCount
Add WHERE clause if needed to restrict date ranges. (edit- simplified to use year() and month() functions.)

I haven't check the syntax or performance but you can do something like this,
WITH cte (
countvalue
,description
)
AS (
SELECT COUNT(*)
,'mailCount1'
FROM [WebContact].[dbo].[memberEmails]
WHERE contactdatetime > '01/01/06'
AND contactdatetime < '02/01/06'
UNION ALL
SELECT COUNT(*)
,'mailCount2'
FROM [WebContact].[dbo].[otherEmails]
WHERE contactdatetime > '01/01/06'
AND contactdatetime < '02/01/06'
UNION ALL
SELECT COUNT(*)
,'mailCount3'
FROM [WebContact].[dbo].[memberEmails]
WHERE contactdatetime > '02/01/06'
AND contactdatetime < '03/01/06'
UNION ALL
SELECT COUNT(*)
,'mailCount4'
FROM [WebContact].[dbo].[otherEmails]
WHERE contactdatetime > '02/01/06'
AND contactdatetime < '03/01/06'
)
SELECT mailCount1
,mailCount2
,mailCount3
,mailCount4
FROM (
SELECT countvalue
,description
FROM cte
) d
pivot(max(countvalue) FOR description IN (mailCount1, mailCount2, mailCount3, mailCount4)) piv;
Hope this helps..

Related

conditional running sum

I'm trying to return the number of unique users that converted over time.
So I have the following query:
WITH CTE
As
(
SELECT '2020-04-01' as date,'userA' as user,1 as goals Union all
SELECT '2020-04-01','userB',0 Union all
SELECT '2020-04-01','userC',0 Union all
SELECT '2020-04-03','userA',1 Union all
SELECT '2020-04-05','userC',1 Union all
SELECT '2020-04-06','userC',0 Union all
SELECT '2020-04-06','userB',0
)
select
date,
COUNT(DISTINCT
IF
(goals >= 1,
user,
NULL)) AS cad_converters
from CTE
group by date
I'm trying to count distinct user but I need to find a way to apply the distinct count to the whole date. I probably need to do something like a cumulative some...
expected result would be something like this
date, goals, total_unique_converted_users
'2020-04-01',1,1
'2020-04-01',0,1
'2020-04-01',0,1
'2020-04-03',1,2
'2020-04-05',1,2
'2020-04-06',0,2
'2020-04-06',0,2
Below is for BigQuery Standard SQL
#standardSQL
SELECT t.date, t.goals, total_unique_converted_users
FROM `project.dataset.table` t
LEFT JOIN (
SELECT a.date,
COUNT(DISTINCT IF(b.goals >= 1, b.user, NULL)) AS total_unique_converted_users
FROM `project.dataset.table` a
CROSS JOIN `project.dataset.table` b
WHERE a.date >= b.date
GROUP BY a.date
)
USING(date)
I would approach this by tagging when the first goal is scored for each name. Then simply do a cumulative sum:
select cte.* except (seqnum), countif(seqnum = 1) over (order by date)
from (select cte.*,
(case when goals = 1 then row_number() over (partition by user, goals order by date) end) as seqnum
from cte
) cte;
I realize this can be expressed without the case in the subquery:
select cte.* except (seqnum), countif(seqnum = 1 and goals = 1) over (order by date)
from (select cte.*,
row_number() over (partition by user, goals order by date) as seqnum
from cte
) cte;

Two selects based on one select in on query

Can I do one select and then do different selects on the result in one query?
Now I want to do something like that (which is not working)
select
(select count(*), sum(amount) from view where amount > 5),
(select count(*), sum(amount) from view where amount < 5)
from
(select id, amount from warehouse where createDate = '2019-01-01') as view;
I don't want to select view and then select some data with additional filtering based on the view.
You can use conditional aggregation:
select count(*),
sum(amount) filter (where waga > 5),
sum(amount) filter (where amount < 5)
from warehouse
where createdate = date '2019-01-01'
About the general syntax question you could use WITH clause:
with v as
(
select id, amount from warehouse where createDate = '2019-01-01'
)
select * from
(
(select count(*), sum(amount) from v where waga > 5) as count1,
(select count(*), sum(amount) from v where amount < 5) as count2
);
(I don't mean it will be faster; it's just a way to use an "inline" view).

SQLite Getting multiple results with LIMIT 1

I have the following problem.
Part of a task is to determine the visitor(s) with the most money spent between 2000 and 2020.
It just looks like this.
SELECT UserEMail FROM Visitor
JOIN Ticket ON Visitor.UserEMail = Ticket.VisitorUserEMail
where Ticket.Date> date('2000-01-01') AND Ticket.Date < date ('2020-12-31')
Group by Ticket.VisitorUserEMail
order by SUM(Price) DESC;
Is it possible to output more than one person if both have spent the same amount?
Use rank():
SELECT VisitorUserEMail
FROM (SELECT VisitorUserEMail, SUM(PRICE) as sum_price,
RANK() OVER (ORDER BY SUM(Price) DESC) as seqnum
FROM Ticket t
WHERE t.Date >= date('2000-01-01') AND Ticket.Date <= date('2021-01-01')
GROUP BY t.VisitorUserEMail
) t
WHERE seqnum = 1;
Note: You don't need the JOIN, assuming that ticket buyers are actually visitors. If that assumption is not true, then use the JOIN.
Use a CTE that returns all the total prices for each email and with NOT EXISTS select the rows with the top total price:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT c.VisitorUserEMail
FROM cte c
WHERE NOT EXISTS (
SELECT 1 FROM cte
WHERE SumPrice > c.SumPrice
)
or:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT VisitorUserEMail
FROM cte
WHERE SumPrice = (SELECT MAX(SumPrice) FROM cte)
Note that you don't need the function date() because the result of date('2000-01-01') is '2000-01-01'.
Also I think that the conditions in the WHERE clause should include the =, right?

Invalid column reference in hive

When I run the below query, I get the error "Invalid column reference: cnt". Any suggestions would be great !!
select count(customer) as cnt from (
select customer, concat(visid, lowid), count(name)
from tab1 where date_time between '2017-05-01 00:00:00' and '2017-05-31 23:59:59' and name in ('payment: Complete', 'check: Complete')
group by evar71, concat(visid, lowid)) t1
where cnt > 1;
Another way to do it.
select count(customer) as cnt from (
select customer, concat(visid, lowid), count(name)
from tab1 where date_time between '2017-05-01 00:00:00' and '2017-05-31 23:59:59' and name in ('payment: Complete', 'check: Complete')
group by evar71, concat(visid, lowid)) t1
having count(customer) > 1;
WHERE filter applied before aggregation
that is why where cnt > 1 does not work. There is HAVING keyword which introduces a condition on aggregations, it works as filter after aggregation.
select count(customer) cnt
...
where rows_filter_condition_here --before aggregation
having count(customer) > 1 --aggregation results filter
order by cnt desc --this works after aggregation
I think hive prefers aliases in the group by. In addition, several column aliases are not correct:
select count(customer) as cnt
from (select customer, concat(visid, lowid) as ids, count(name) as cc
from tab1
where date_time >= '2017-05-01' and date_time < '2017-06-01' and
name in ('payment: Complete', 'check: Complete')
group by customer, ids
) t1
where cc > 1;

sql query - find distinct user from table

I am trying to solve a problem using SQL query and need some expert's advice.
I have below transaction table.
-- UserID, ProductId, TransactionDate
-- 1 , 2 , 2014-01-01
-- 1 , 3 , 2014-01-05
-- 2 , 2 , 2014-01-02
-- 2 , 3 , 2014-05-07
.
.
.
What I am trying to achieve is to find all user who purchased more than one product WITHIN 30 DAYS .
My query so far is like
select UserID, COUNT(distinct ProductID)
from tableA
GROUP BY UserID HAVING COUNT(distinct ProductID) > 1
I am not sure where to apply "WITH IN 30 DAYS" logic in the query .
The outcome should be :
1, 2
2, 1
Thanks in advance for your help.
Edit: Within 30 Days
SQL Fiddle
SELECT
a.UserID,
COUNT(DISTINCT ProductID)
FROM TableA a
INNER JOIN (
SELECT UserID, TransactionDate = MAX(TransactionDate)
FROM TableA
GROUP BY UserID
) AS t
ON t.UserID = a.UserID
AND a.TransactionDate >= DATEADD(DAY, -30, t.TransactionDate)
AND a.TransactionDate <= t.TransactionDate
GROUP BY a.UserID
You can use GROUP BY YEAR(TransactionDate), MONTH(TransactionDate)
SELECT
UserID,
COUNT(DISTINCT ProductID)
FROM TableA
GROUP BY
UserID, YEAR(TransactionDate), MONTH(TransactionDate)
HAVING
COUNT(DISTINCT ProductID) > 1
Just add a where clause.
SELECT UserID, COUNT(DISTINCT ProductID) cnt
FROM tableA
WHERE TransactionDate >= CAST(DATEADD(DAY,-30,GETDATE()) AS DATE)
GROUP BY UserID
HAVING COUNT(DISTINCT ProductID) > 1
This works because the where clause is performed BEFORE the Group By and Having. So first it filters out all transactions over 30 days old and then returns only people who bought two distinct products.
Query Processing Order:
http://blog.sqlauthority.com/2009/04/06/sql-server-logical-query-processing-phases-order-of-statement-execution/