SQL sum, grouping by another table - sql

Consider I have a table t1 with a single column a, and it has values, say
a
-
5
10
15
17
I want to write a single SQL query that does the following. Basically, I want to know the sum of all values up to the value in the table t1.
SELECT SUM(value) FROM t2 WHERE value<=5
UNION ALL
SELECT SUM(value) FROM t2 WHERE value<=10
UNION ALL
SELECT SUM(value) FROM t2 WHERE value<=15
UNION ALL
SELECT SUM(value) FROM t2 WHERE value<=17;
If someone changes the value in a, like delete or insert more elements, I have to rewrite the above query. Is there a query that always works automatically?
Here is the DB fiddle link.

I think you appears to want :
SELECT SUM(case when bound<=a[1] then value else 0 end),
. . .
FROM table t;
After edit with fiddle, you can use subquery instead of UNION :
select *, (select sum(t2.value) from t2 where t2.value <= t1.a)
from t1;

I think it's more clear with a join than a subquery, but just personal preference.
SELECT
t1.a,
SUM(COALESCE(t2.value, 0))
FROM
t1
LEFT JOIN
t2
ON
t2.value <= t1.a
GROUP BY
t1.a

Related

How to get also the not existing values

I've got a query like this
select column, count(*)
from mytable
where column in ('XXX','YYY','ZZZ',....)
group by column;
But I want also to get a row for values the aren't in the table.
Let's suppose that 'ZZZ' doesn't exist in mytable, I'd like to get:
COLUMN COUNT(*)
XXX 3
YYY 2
ZZZ 0 (or NULL)
Oracle version 10g
Thanks in advance
Mark
In general, you would need to have a second table which contains all the possible column values whose counts you want to appear in the output. For demo purposes only, we can use a CTE for that:
WITH vals AS (
SELECT 'XXX' AS val UNION ALL
SELECT 'YYY' UNION ALL
SELECT 'ZZZ'
)
SELECT t1.val, COUNT(t2.col) AS cnt
FROM vals t1
LEFT JOIN mytable t2
ON t2.col = t1.val
GROUP BY
t1.val;

How to handle duplicate columns in multiple WITH clause SQL

I am trying to create a query with multiple WITH clause in bigquery. I am getting an error : Duplicate column names in the result are not supported. Found duplicate(s): because I have some repeated columns in the tables.
Problem is I can't remove them as I need it to display in my table also they are needed in the group by clause in the tables.
My code somewhat looks like this:
WITH table0 as (## query0),
table1 AS (## query1),
table2 as (## query2),
table3 as (## query3),
table4 as (## query4),
table5 as (## query 5)
select
*
from
table0,
table1,
table2,
table3,
table4,
table5
How do I handle duplicate columns in multiple WITH clause in SQL
Why are you creating a Cartesian product of the subqueries?
In any case, BigQuery gives you more control over the columns than other databases. So, if col1 is common to table0 and table1, you can do:
select t1.*, t2.* except (col1)
If you want to keep both values:
select t1.*, t2.* except (col1), t2.col1 as t2_col1
or
select t1.* except (col1),
t2.* except (col1),
t1.col1 as t1_col1,
t2.col1 as t2_col1
From your previous question (that looks like was deleted) I think I remember your use case and there you had (I can be wrong ) only fields that are used for JOINs are "duplicate"
In such cases you can use below approach (in below example it is assumed that those duplicate fields are id and day)
#standardSQL
SELECT *
FROM `project.dataset.table0`
JOIN `project.dataset.table1` USING(id, day)
JOIN `project.dataset.table2` USING(id, day)
JOIN `project.dataset.table3` USING(id, day)
for example in below super-simplified dummy example
#standardSQL
WITH `project.dataset.table0` AS (
SELECT 1 id, '2019-01-01' day, 0 col0
), `project.dataset.table1` AS (
SELECT 1 id, '2019-01-01' day, 1 col1
), `project.dataset.table2` AS (
SELECT 1 id, '2019-01-01' day, 2 col2
), `project.dataset.table3` AS (
SELECT 1 id, '2019-01-01' day, 3 col3
)
SELECT *
FROM `project.dataset.table0`
JOIN `project.dataset.table1` USING(id, day)
JOIN `project.dataset.table2` USING(id, day)
JOIN `project.dataset.table3` USING(id, day)
result will be
Row id day col0 col1 col2 col3
1 1 2019-01-01 0 1 2 3
w/o any complains about duplicate fields
As you can see from above example - using USING() instead of ON "magicaly" resolves the issue - but again - note - only for case when "duplicate" fields are all JOIN fields

Sum values from different tables

I read some topics about this but I'm not very good with sql. I have 10 tables with these fields:
value
type
date
I want to sum all the value fileds together when they have a specific type. I was trying to do something like this, but it's not working.
select sum(tab1.value) + sum(tab2.value)
from tab1, tab2
where tab1.type = tab2.type = 'box'
I guess I could do many simple queries like these and then sum all the results
select sum(value) from tab1 where type='box'
select sum(value) from tab2 where type='box'
but I wonder if I can do one single query
thanks
Having multiple tables with the same structure is usually a sign of poor database design.
I would suggest that you use your last approach, but put the subqueries in the from clause and then add the results in the select:
select t1.value + t2.value + . .
from (select sum(value) as value from tab1 where type='box') t1 cross join
(select sum(value) as value from tab2 where type='box') t2 cross join
. . .
Alternatively, you could union all them together in the from clause and then take the sum:
select sum(value)
from ((select sum(value) as value from tab1 where type='box') union all
(select sum(value) as value from tab2 where type='box') union all
. . .
) t;
If the tables are not linked via FK/PK you can use multiple sub-queries:
SELECT (SELECT SUM(tab1.value) FROM tab1 WHERE type='box') as Tab1Sum,
(SELECT SUM(tab2.value) FROM tab2 WHERE type='box') as Tab2Sum -- and so on...
This yields a single record where each column is the sum of each table.
1.Use single select;
DECLARE #type NVARCHAR(255) = N'Box';
SELECT (SELECT SUM(value) FROM tab1 WHERE type=#Box)
+ (SELECT SUM(value) FROM tab2 WHERE type=#Box)
+ (SELECT SUM(value) FROM tab3 WHERE type=#Box)
+ (...)
I think it's simplest one.
2.you create a view as
CREATE VIEW tabs
AS
SELECT value, type FROM tab1
UNION
SELECT value, type FROM tab2
UNION
SELECT value, type FROM tab3
UNION
...
Then
SELECT SUM(value) FROM tabs WHERE type = 'BOX'
3.Think why similar column are different tables. Can they be merged into single table?
If answer is No, and you have too many tables, consider concatenate SQL strings and use sp_executesql to execute it.

SQL question: Getting records based on datediff from record to record

Ok, got a tricky one here... If my data looks like this:
Table1
ID Date_Created
1 1/1/2009
2 1/3/2009
3 1/5/2009
4 1/10/2009
5 1/15/2009
6 1/16/2009
How do I get the records that are 2 days apart from each other? My end result set should be rows 1-3, and 5-6. Thanks!
SELECT l.*
FROM Table1 l
INNER JOIN Table1 r ON DATEDIFF(d, l.Date_Created, r.Date_Created) = 2
AND r.Date_Created = (SELECT TOP 1 * FROM Table1 WHERE Date_Created > l.Date_Created ORDER BY Date_Create)
select distinct t1.*
from Table1 t1
inner join Table1 t2
on abs(cast(t1.Date_Created - t2.Date_Created as float)) between 1 and 2
-- what does this give you?
select DISTINCT t1.id, t1.date_created, t2.id, t2.date_created from table1 t1, table1 t2 where datediff(dd,t1.date_created,t2.date_created) = 2 AND t1.id != t2.id ORDER BY t1.id;
Would this work?
select t1.id, t2.id
from table1 t1
join table1 t2
on t2.date_created - t1.date_created <= 2
I might suggest using programming code to do it. You want to collect groups of rows (separate groups). I don't think you can solve this using a single query (which would give you just one set of rows back).
If you want to get the rows which are WITHIN 'N' days apart, you can try this:
select t1.date_created, t2.date_created
from table1 t1, table1 t2
where t1.id <> t2.id and
t2.date_created-t1.date_created between 0 and N;
for exmaple, as you said, if you want to get the rows which are WITHIN 2 days a part,
you can use the below:
select t1.date_created,t2.date_created
from table1 t1, table1.t2
where t1.id <> t2.id and
t2.date_created-t1.date_created between 0 and 2;
I hope this helps....
Regards,
Srikrishna.
A cursor will be fastest, but here is a SELECT query that will do it. Note that for "up to N" days apart instead of 2 you'll have to replace the table Two with a table of integers from 0 to N-1 (and the efficiency will get worse).
I'll admit it's not entirely clear what you want, but I'm guess you want the ranges of rows that contain at least two rows in all and within which the successive rows are at most 2 days apart. If dates increase along with IDs, this should work.
with Two as (
select 0 as offset union all select 1
), r2(ID, Date_Created_o, dr) as (
select
ID, Date_Created+offset,
Date_Created + offset - dense_rank() over (
order by Date_Created+offset
) from r cross join Two
)
select
min(ID) as start, max(ID) as finish
from r2
group by dr
having min(ID) < max(ID)
order by dr;

SQL create a temporary 'mapping' table in a select statement

I'm building up results by joining tables
select t1.*, t2.col2 from t1, t2 where t1.col1=t2.col1
Is there a way to create a temporary 'mapping' table 'inline' in a select statement for instances where the t2 table doesn't exist?
So something like
select t1.*, tempt2.col2 from t1, (<create temp table>) tempt2 where ...
I'm using Oracle
Table with 1 row:
SELECT t1.*, t2.col2
FROM t1,
(
SELECT 1 AS col2
FROM dual
) t2
Table with 0 rows:
SELECT t1.*, t2.col2
FROM t1,
(
SELECT 1 AS col2
FROM dual
WHERE 1 = 0
) t2
Table with N rows:
SELECT t1.*, t2.col2
FROM t1,
(
SELECT 1 AS col2
FROM dual
CONNECT BY
level <= :N
) t2
I'm not sure this is what you're looking for, but you can do multiple SELECTs with UNIONs to get a derived table
SELECT t1.*, t2.col2
FROM t1, (
SELECT 1 as Id, 'Foo' as Col2
UNION ALL
SELECT 2 as Id, 'Bar' as Col2
UNION ALL
SELECT 3 as Id, 'FooBar' as Col2
) t2
WHERE
t1.Id = t2.Id
I know that this question is very old. But just to complete the answer, what about the DECODE() function?
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions040.htm#i1017437
SELECT product_id,
DECODE (warehouse_id, 1, 'Southlake',
2, 'San Francisco',
3, 'New Jersey',
4, 'Seattle',
'Non domestic')
"Location of inventory" FROM inventories
WHERE product_id < 1775;
As long as the mapping table is reasonably short this seems like a pretty elegant solution.
Don't know whether it works in Oracle, but are you looking for something like the following pseudocode?
select t1.*, tempt2.col2 from t1 inner join (select col2, foo, bar from t2 where bar = ?) tempt2 on t1.foo = tempt2.foo where . . .
I guess that doesn't really solve the problem, since you said that table2 (t2) doesn't really exist. I'm not sure what you'd have in your mapping table or where you'd get that data if not from a table.
I'm not totally sure what you're getting at here.
Either what you want is the WITH clause e.g.
WITH tempt2 AS
(SELECT x FROM y)
SELECT t1.*, tempt2.col2
FROM t1, tempt2
WHERE ...
Or else if you're running the same SQL on different DBs and you can't be sure that the table actually exists, then you would probably be better to test for its presence and react differently.
Does it need to pure SQL or can you use PL/SQL?