Google big query count - sql

I am trying to pull all the customers having less than 4 orders
in past 3 months in Google BigQuery.
SELECT a.user_id, b.refer_by, FROM water_db.tb_order a INNER JOIN
water_auth.tb_users b ON a.user_id = b.user_id WHERE ( SELECT
user_id FROM
water_db.tb_order GROUP BY
user_id HAVING
COUNT(DISTINCT(a.user_id <= 4))) AND status = 3 AND DATE(a.order_date) >=
'2017-02-15' AND DATE(a.order_date) <= '2017-05-15';------

I'm guessing that each time a record is added to the table it equates to an order but something like:
SELECT
a.userid, b.refer_by
FROM water_db.tb_order a
INNER JOIN water_auth.tb_users b ON a.user_id = b.user_id
WHERE
(COUNT(userid) < 4)
and
DATE_ADD(MONTH, -4, a.order_date)
The date function may differ as I'm not 100% sure what it is in Google Big Query

I think the best way to approach this is by selecting from the users table, so you don't need to deduplicate IDs, and just expressing the condition as part of your WHERE clause. This should help get you started:
#standardSQL
SELECT
user_id,
refer_by
FROM water_db.tb_users
WHERE (
SELECT COUNT(*)
FROM water_db.tb_order
WHERE tb_users.user_id = tb_order.user_id AND
status = 3 AND
DATE(order_date) BETWEEN '2017-02-15' AND '2017-05-15'
) <= 4;
In this query, the join is expressed as a correlated subquery involving the two tables. You can try it out using sample data with this query:
#standardSQL
WITH tb_users AS (
SELECT 1 AS user_id, 'foo#bar.com' AS refer_by UNION ALL
SELECT 2, 'a#b.com' UNION ALL
SELECT 3, 'baz#baz.com'
),
tb_order AS (
SELECT 1 AS user_id, TIMESTAMP '2017-04-12' AS order_date, 3 AS status UNION ALL
SELECT 2, TIMESTAMP '2017-05-03', 3 UNION ALL
SELECT 1, TIMESTAMP '2017-03-13', 3 UNION ALL
SELECT 1, TIMESTAMP '2017-02-28', 3 UNION ALL
SELECT 2, TIMESTAMP '2017-05-06', 3 UNION ALL
SELECT 1, TIMESTAMP '2017-05-01', 3 UNION ALL
SELECT 1, TIMESTAMP '2017-05-02', 3
)
SELECT
user_id,
refer_by
FROM tb_users
WHERE (
SELECT COUNT(*)
FROM tb_order
WHERE tb_users.user_id = tb_order.user_id AND
status = 3 AND
DATE(order_date) BETWEEN
'2017-02-15' AND '2017-05-15'
) <= 4;

Related

How to compare different values within the same column

I having two tables emp and type.
create table EMP(ID number(10), effective_date date);
EID Effective_date
--------------------
1 02/14/2023
2 02/15/2023
3 04/30/2023
4 03/24/2023
create table type(ID number(10),contract_type varchar2(2));
TID contract_type
------------------
1 P
1 S
1 P
2 S
2 S
3 P
3 S
4 S
I am looking EID which is having contract type is 'S' in type table. (or emp table with effective date is greater than sysdate and in the type table with only contract_type ='S')
Actual result :
2
4
My query is not giving the correct results.
select emp.EID
from emp,type
where EID = TID
contract_type ='S'
effective_date >= sysdate
group by TID
having count(TID) >= 1;
If you want to keep your idea with COUNT and GROUP BY, you should count other contract types than the 'S' ones and check this is 0:
SELECT e.eid
FROM emp e
JOIN type t ON e.eid = t.tid
WHERE
e.effective_date >= sysdate
GROUP BY e.eid
HAVING COUNT(CASE WHEN t.contract_type <> 'S' THEN 1 END) = 0;
This query will return 2 and 4 for your sample data.
Try out: db<>fiddle
Another option is as already said here using NOT EXISTS.
Take care of following difference to the NOT EXISTS approach: The query in Tim's answer will also fetch id's of table "emp" that don't appear at all in table "type". My query here will not fetch such id's.
It's up to you to decide whether this is possible at all and what to do in this case.
Changing JOIN to LEFT JOIN in above query will eliminate this difference.
I would use exists logic here:
SELECT EID
FROM EMP e
WHERE effective_date >= SYSDATE AND
NOT EXISTS (
SELECT 1
FROM "type" t
WHERE t.TID = e.EID AND
t.contract_type <> 'S'
);
You could use Count() Over() analytic function to check for type 'S' and number of different types per ID.
SELECT DISTINCT ID
FROM ( Select e.EID "ID",
Count(CASE t.CONTRACT_TYPE WHEN 'S' THEN 'S' END) Over(Partition By t.ID Order By t.ID) "NUM_OF_S",
Count(Distinct t.CONTRACT_TYPE) Over(Partition By t.ID) "NUM_OF_TYPES",
TRUNC(e.EFFECTIVE_DATE) - TRUNC(SYSDATE) "DAYS_AFTER_SYSDATE"
From emp_cte e
Inner Join type_cte t ON(t.ID = e.EID) )
WHERE NUM_OF_S > 0 And -- Type 'S' exists for ID AND
NUM_OF_TYPES = 1 And -- It is the only type AND
DAYS_AFTER_SYSDATE > 0 -- EFFECTIVE_DATE is after SYSDATE
With your sample data ...
WITH
emp_cte(EID, EFFECTIVE_DATE) AS
(
Select 1, To_Date('02/14/2023', 'mm/dd/yyyy') From Dual Union All
Select 2, To_Date('02/15/2023', 'mm/dd/yyyy') From Dual Union All
Select 3, To_Date('04/30/2023', 'mm/dd/yyyy') From Dual Union All
Select 4, To_Date('03/24/2023', 'mm/dd/yyyy') From Dual
),
type_cte(ID, CONTRACT_TYPE) AS
(
Select 1, 'P' From Dual Union All
Select 1, 'S' From Dual Union All
Select 1, 'P' From Dual Union All
Select 2, 'S' From Dual Union All
Select 2, 'S' From Dual Union All
Select 3, 'P' From Dual Union All
Select 3, 'S' From Dual Union All
Select 4, 'S' From Dual
)
... result would be ...
-- ID
-- ----------
-- 2
-- 4

Oracle SQL - Count based on a condition to include distinct rows with zero matches

Is there a "better" way to refactor the query below that returns the number occurrences of a particular value (e.g. 'A') for each distinct id? The challenge seems to be keeping id = 2 in the result set even though the count is zero (id = 2 is never related to 'A'). It has a common table expression, NVL function, in-line view, distinct, and left join. Is all of that really needed to get this job done? (Oracle 19c)
create table T (id, val) as
select 1, 'A' from dual
union all select 1, 'B' from dual
union all select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 2, 'B' from dual
union all select 3, 'A' from dual
;
with C as (select id, val, count(*) cnt from T where val = 'A' group by id, val)
select D.id, nvl(C.cnt, 0) cnt_with_zero from (select distinct id from T) D left join C on D.id = C.id
order by id
;
ID CNT_WITH_ZERO
---------- -------------
1 2
2 0
3 1
A simple way is conditional aggregation:
select id,
sum(case when val = 'A' then 1 else 0 end) as num_As
from t
group by id;
If you have another table with one row per id, you I would recommend:
select i.id,
(select count(*) from t where t.id = i.id and t.val = 'A') as num_As
from ids i;

How to select a row after group by unioned tables?

I need to select the newest row from two tables, two tables have the same schema
Table A and Table B is the same schema, like this:
Table A :
user_id, time_stamp, order_id
1,20190101,100
2,20190103,201
3,20190102,300
5,20180209,99
Table B:
user_id, time_stamp, order_id
1,20190102,101
2,20190101,200
3,20190103,305
4,20190303,900
I want the output is A union B, then select the newer row of a user, order by time_stamp:
output should be:
1,20190102,101
2,20190103,201
3,20190103,305
4,20190303,900
5,20180209,99
How to write this SQL?
You can write as following sample query demo
with unionedTable as (
select * from tableA
union
select * from tableB)
,newerUsersTable as (
select distinct on (u.user_id)u.*
from unionedTable u
order by u.user_id, u.time_stamp desc
)select * from newerUsersTable
The main idea is using FULL OUTER JOIN among two tables, and then using UNION [ALL] for returning data set. So, consider the following SELECT statement with WITH clause :
with a( user_id, time_stamp, order_id ) as
(
select 1,20190101,100 union all
select 2,20190103,201 union all
select 3,20190102,300 union all
select 5,20180209,99
), b( user_id, time_stamp, order_id ) as
(
select 1,20190102,101 union all
select 2,20190101,200 union all
select 3,20190103,305 union all
select 4,20190303,900
), c as
(
select a.user_id as user_id_a, a.time_stamp as time_stamp_a, a.order_id as order_id_a,
b.user_id as user_id_b, b.time_stamp as time_stamp_b, b.order_id as order_id_b
from a full outer join b
on a.user_id = b.user_id
), d as
(
select user_id_a, time_stamp_a, order_id_a
from c
where coalesce(time_stamp_b,time_stamp_a) <= time_stamp_a
union all
select user_id_b, time_stamp_b, order_id_b
from c
where time_stamp_b >= coalesce(time_stamp_a,time_stamp_b)
)
select user_id_a as user_id, time_stamp_a as time_stamp, order_id_a as order_id
from d
order by user_id_a;
user_id time_stamp order_id
1 20190102 101
2 20190103 201
3 20190103 305
4 20190303 900
5 20180209 99
Demo
Use Group by(user_id) to show all user_id
Use max(time_stamp) get the newer row of user
SELECT aa.* from (select * from a union SELECT * from b ) aa
JOIN
(select user_id,max(time_stamp) as new_time
from (select * from a union SELECT * from b ) u
group by u.user_id) bb
on bb.new_time=aa.time_stamp and bb.user_id=aa.user_id
order by aa.user_id;
SQL Fiddle
I would simply do:
select user_id, time_stamp, order_id
from (select ab.*,
row_number() over (partition by user_id order by time_stamp desc) as seqnum
from (select a.* from a union all
select b.* from b
) ab
) ab
where seqnum = 1;

Move columns to rows

I need to add the third column to the first column (my desire is that the first column will include also the third column)
Current status:
Desired Results:
You want UNION ALL :
SELECT t.entity, t.activity
FROM table t
UNION ALL
SELECT t.entity2, t.activity2
FROM table t;
If you have a lot of data, you may not want to scan the table multiple times -- which is what union all does.
Instead:
select (case when n.n = 1 then entity
when n.n = 2 then entity_2
end) as entity,
(case when n.n = 1 then activity
when n.n = 2 then activity_2
end) as activity
from t cross join
(select 1 as n from dual union all
select 2 as n from dual
) n;
In Oracle 12C+, this is simplified using lateral joins:
select t.entity, s.activity
from t cross join lateral
(select t.entity, t.activity from dual union all
select t.entity_2, t.activity_2 from dual
) s;
select entity, activity from <table>
union all
select entity_2, activity_2 from <table>
in general:
select col1,col2 from table1
union all
select col3,col4 form table1;

sqlite - how to combine overall count and group by?

I have the following table of PHOTOS, their times and months:
TIME|MONTH|PHOTO
----|-----|-----
x1 | mx1 | p1
x2 | mx2 | p2
...
I'd like to get all months sorted descendent BUT, with the total photos count up until that month - not just the count of that month's group.
For example, the following query isn't good enough as it returns the count of each specific month's group instead of the total count up until that group:
select MONTH, count(TIME) from PHOTOS group by MONTH sort by MONTH desc
Ideas?
SQLite has less functionalities compared to other RDBMS but I think this is how you should do it:
SELECT photosA.month,
(SELECT COUNT(time) AS PhotoCounter
FROM photos AS photosB
WHERE photosB.month <= photosA.month) AS total_photos
FROM photos AS photosA
GROUP BY photosA.month
ciao!
select a.month, count(b.month) as CountSmaller
from [PHOTOS] a
inner join [PHOTOS] b on b.[MONTH] <= a.[MONTH]
group by a.time, a.month, a.photo
order by 2 desc
tested in sqlfiddle
Fiddle http://sqlfiddle.com/#!7/812ac/2/0
To solve your problem, firstly you need some kind of month order. You can do it by this query:
select 1 id, 'mx1' m
union
select 2, 'mx2'
union
select 3, 'mx3'
union
select 4, 'mx4'
You can do it in every query by nested query or create separate table.
And manipulating nested queries, aggregate function and joins you can achieve your goal by:
select m2,
(
select count(*) from photos p
inner join
(
select 1 id, 'mx1' month
union
select 2, 'mx2'
union
select 3, 'mx3'
union
select 4, 'mx4'
) m
on p.month = m.month and m.id <= M.id2
)
from
(
select 1 id2, 'mx1' m2
union
select 2, 'mx2'
union
select 3, 'mx3'
union
select 4, 'mx4'
) M
If you have any questions I can answer them in comments.