Merge two almost identical UNIONed queries into one - sql

I have multiple queries nested together by UNION ALLs; some of the inner queries are almost the same.
For example
select sum(x.amount) as amnt, 'txt1' as name, x.cfg as cfg from tbl1
union all
select -sum(x.amount) as amnt, 'txt2' as name, x.cfg as cfg from tbl1
result:
AMNT|NAME|CFG
----+----+---
12 |txt1| Z
-12 |tst2| Z
Since the inner queries are not that small and go to a lot of tables themselves I'm trying to save processing time and resources by combining these two inner queries into one. Take in consideration that the NAME (txt1/txt2) is on the inner query and not in a table

For this particular example, you need to duplicate the results returned, with some conditional logic. If you put the conditional logic into a CTE then perform a Cartesian join against your main table then every row in the main table will be duplicated by the number of records in the join. In this case that would be 2.
with multiplier (m, name) as (
select 1, 'txt1' from dual
union all
select -1, 'txt2' from dual
)
select multiplier.m * sum(t.amount), multiplier.name, t.cfg
from tbl1 t
cross join multiplier

Related

SQL count(distinct) from both the table

I have 2 tables. Let's say Table A and Table B. Table A has a column called "name". Table B also has a column "name". I want to find out the count(distinct name). Name should take values from both the columns.
For ex-
Table A
name
A
B
C
Table B
name
A
B
D
Output should be 4.
The best concept is, first combine the data in the way you want using a subquery, and then dedupe or do the 2nd step.
For example,
WITH COMBINED AS (
SELECT
name
FROM
TableA
UNION ALL
SELECT
name
FROM
TableB
)
SELECT
DISTINCT name
FROM
COMBINED
In your situation, the 2nd step can be accomplished by changing UNION ALL to a UNION. This will dedupe the values automatically. You won't even need a subquery or a 2nd step. But I wanted to teach you the concept because it comes up often.
SELECT name FROM TableA
UNION
SELECT name FROM TableB
Then UNION in the CTE will reove all Duplicates
so a COUNT(*) will suffoce
WITH CTE AS (
SELECT name FROM TableA
UNION
SELECT name FROM TableB
)
SELECT COUNT(*) FROM CTE
I hope this query should do it:
SELECT SUM(names) AS total_names
FROM (
SELECT COUNT(DISTINCT(name)) as names FROM TableA
UNION
SELECT COUNT(DISTINCT(name)) as names FROM TableB
) t;
Note: Tested with sql server
Yet another option:
select hll_count.merge(hll_sketch) names
from (
select hll_count.init(name) hll_sketch from tableA
union all
select hll_count.init(name) from tableB
)
HLL++ functions are approximate aggregate functions. Approximate aggregation typically requires less memory than exact aggregation functions, like COUNT(DISTINCT), but also introduces statistical error. This makes HLL++ functions appropriate for large data streams for which linear memory usage is impractical, as well as for data that is already approximate.
See more about benefits of using HyperLogLog++ functions

Select unmatching rows from two tables grouping by two columns

I have two tables with sales information that have different number of rows and I want to get these rows. An important thing to notice is that records are added to the tables by the key of two columns: sale_type and sale_date.
So I think I should group by these columns after making a Union of the two tables. And filter by count. But my current solution does not work. How should I fetch the unmatching records correctly?
Here is what I've tried:
SELECT * FROM
(SELECT * FROM sales_copy
UNION ALL
SELECT * FROM sales)
GROUP BY sale_type, sale_date
HAVING count(*)!=1;
To select the difference between two tables, I've managed to do this successfully using a full join.
So in your case, you would want something like:
SELECT S.*, SC.*
FROM sales AS S
FULL JOIN sales_copy AS SC ON (S.sale_type = SC.sale_type) AND (S.sale_date =
SC.sale_date
WHERE (S.sale_type IS NULL AND S.sale_date IS NULL) OR (SC.sale_type IS NULL AND
SC.sale_date IS NULL)
The result of this will select all the rows which are in one table only, ignoring the rows which are in both.
See it in action here: SQL Fiddle
You can use both EXCEPT and UNION operator :
SELECT sale_type, sale_date FROM sales EXCEPT SELECT sale_type,sale_date FROM sales_copy
UNION
SELECT sale_type, sale_date FROM sales_copy EXCEPT SELECT sale_type,sale_date FROM sales
It returns rows from sales wich are not in sales_copy and rows from sales_copy wich are not in sales
The same thing can be achieved with a full join by filtering rows wich are matching:
SELECT ISNULL(sales.sale_type, sales_copy.sale_type) AS sale_type
, ISNULL(sales.sale_date, sales_copy.sale_date) AS sale_date
FROM sales
FULL JOIN sales_copy
ON sales.sale_type = sales_copy.sale_type
AND sales.sale_date = sales_copy.sale_date
WHERE sales.sale_type IS NULL
OR sales_copy.sale_type IS NULL

Join two select statements together

I am trying to work out how much we have taken in for entry fees.
I have two separate queries both returning values but i need them be as one instead of two separate queries.
SELECT SUM(ENTRY) AS TOTAL1 FROM MONEY
SELECT SUM(ENTRY) AS TOTAL1 FROM MONEY2
I needed to use UNION in order to get the statements together. Then used the below to get one number.
SELECT SUM(X.TOTAL1) from
(
SELECT SUM(ENTRY) AS TOTAL1 FROM MONEY
UNION
SELECT SUM(ENTRY) AS TOTAL1 FROM MONEY2
) X;
select sum(entry) as grand_total
from ( select entry from money
union all
select entry from money2
);
The point being, you SHOULD use UNION ALL; and how many columns each table has is irrelevant, because you don't need to UNION ALL the two tables (all columns from each); you only need to UNION ALL the ENTRY column from the first table and the ENTRY column from the second table.

recursively increment a date in sql

I am working on a historical conversion of data and was wondering if there's a more efficient way to accomplish a date increment.
I receive a data from a source system on a saturday date (1-7-13) and would like to push that data to make it fill all days of the previous week (1-6-13,1-5-13 ect).
So currently i am doing several unions
insert into target
(date, name)
select date,name
from
(
SELECT date as date, name FROM SOURCE
UNION
SELECT date - 1 as date, name FROM SOURCE
UNION
SELECT date -2 as date, name FROM SOURCE
)
I only ask because it looks like close to 500 million records are going to be going though this sql script. Incase it matters it is going to be running in a BTEQ script in TERADATA.
First, your code would be faster using union all rather than union. union removes duplicates, which does not seem to be needed in this case. If you do need them removed, then do it at the source level:
from (select distinct name from source)
Rather than doing it implicitly with union.
You can also try a cross join approach:
select date - i, name
from source cross join
(select 0 as i union all select 1 union all select 2 union all select 3 union all
select 4 union all select 5 union all select 6
) const
This might be a bit faster, because it doesn't need to set up the reads to the table multiple times.
One option is to use a recursive query, but I don't think it would be much faster -- just perhaps easier to read:
WITH RECURSIVE recursiveCTE (date, name) AS (
SELECT date, name
FROM Source
UNION ALL
SELECT r.date-1, r.name
FROM recursiveCTE R
JOIN Source T ON R.name = T.name AND T.date < r.date+6
)
INSERT INTO Target (date,name)
SELECT date,name From recursiveCTE

combine SELECTS in ONE VIEW DISPLAY

I need to know of a way to combine multiple SELECT statements in one VIEW? I tried the UNION ALL, but it fails since I am using unique columns to aggregate the GRAND TOTAL.
I am a student this is part of a group project.
I have one table with 4 columns: account, description, short_description, and balance. The COA (chart of accounts) is an excel spreadsheet that is imported.
CREATE VIEW [account_balance_sums]
AS
SELECT SUM(balance) AS total,
SUBSTRING (Account,0,2) AS account_group
FROM COA
GROUP BY account_group
GO
SELECT * FROM [account_balance_sums]
SELECT SUM(total) AS Grand_total
FROM [account_balance_sums]
Assuming that you are trying to create a view that gives account group and total balance with a single extra row for the total across all accounts then this view should help:
CREATE VIEW [account_balance_sums] AS
SELECT SUM(balance) AS total, SUBSTRING (Account,0,2) AS account_group
FROM COA
GROUP BY account_group
UNION ALL
SELECT SUM(balance), 'Grand Total'
FROM account_group
By the way, the sub-string of the first characters of the account name suggests that you have more than one piece of data in a single column. This indicates a data that is not properly normalised, which you should probably address if you want top marks. See wikipedia on normal form
In a UNION'd statement, there must be:
The same number of columns in each SELECT statement
The data types must match at each position in the SELECT statement
Use:
SELECT *
FROM [account_balance_sums]
UNION ALL
SELECT SUM(total),
NULL AS account_group
FROM [account_balance_sums]
UNION ALL should work. basic structure like this
select a,b,c,d
from t1
union all
select a,b,c,e
from t2
so long as d and e are the same data type.
to do the sum, then you wrap this with the aggregation layer - using this structure as an inline view (among other methods)
something like:
select sum( d )
from (
select a,b,c,d
from t1
union all
select a,b,c,e
from t2
)