SQL Server UNION and offset primary key - sql

I would like to union two tables, and in the results, have a column of the second table be offset by the max value of that column in the first table.
Example: suppose I have two tables where both have the same columns:
TableA
a | b | c
1 | 1 | 1
2 | 2 | 2
TableB
a | b | c
1 | 6 | 6
2 | 7 | 7
I want to be able to perform something like a UNION ALL which results in:
Results
a | b | c
1 | 1 | 1
2 | 2 | 2
3 | 6 | 6
4 | 7 | 7
By performing an actual UNION ALL my results are:
Results
a | b | c
1 | 1 | 1
2 | 2 | 2
1 | 6 | 6
2 | 7 | 7
UPDATE: I would also like to put this into a VIEW, which is complicating it for me.
Thanks in advance

You can get the max a value from table A and add it to column a from table B.
select a,b,c from tblA
union all
select a+t.max_a,b,c from tblB
cross join (select max(a) as max_a from tblA) t

does it value of column [a] for both table needs to follow original value ?
select a = row_number() over (order by a), b, c
from
(
select a, b, c from tableA
union all
select a, b, c from tableB
) d
alternatively, use row_number() to generate a sequence for tableb
select a = coalesce(a, max(a) over() + row_number() over(order by a)),
b, c
from
(
select a, b, c
from tableA
union all
select a = NULL, b, c
from tableB
) d
order by a

Related

Bigquery: Joining 2 tables one having repeated records and one with count ()

I want to join tables after unnest arrays in Table:1 but the records duplicated after the join because of the unnest.
Table:1
| a | d.b | d.c |
-----------------
| 1 | 5 | 2 |
- -------------
| | 3 | 1 |
-----------------
| 2 | 2 | 1 |
Table:2
| a | c | f |
-----------------
| 1 | 12 | 13 |
-----------------
| 2 | 14 | 15 |
I want to join table 1 and 2 on a but I need also to have the output of:
| a | d.b | d.c | f | h | Sum(count(a))
---------------------------------------------
| 1 | 5 | 2 | 13 | 12 |
- ------------- - - 1
| | 3 | 1 | | |
---------------------------------------------
| 2 | 2 | 1 | 15 | 14 | 1
a can be repeated in table 2 for that I need to count(a) then select the sum after join.
My problem is when I'm joining I need the nested and repeated record to be the same as in the first table but when use aggregation to get the sum I can't group by struct or arrays so I UNNEST the records first then use ARRAY_AGG function but also there was an issue in the sum.
SELECT
t1.a,
t2.f,
t2.h,
ARRAY_AGG(DISTINCT(t1.db)) as db,
ARRAY_AGG(DISTINCT(t1.dc)) as dc,
SUM(t2.total) AS total
FROM (
SELECT
a,
d.b as db,
d.c as dc
FROM
`table1`,
UNNEST(d) AS d,
) AS t1
LEFT JOIN (
SELECT
a,
f,
h,
COUNT(*) AS total,
FROM
`table2`
GROUP BY
a,f,h) AS t2
ON
t1.a = t2.a
GROUP BY
1,
2,
3
Note: the error is in the total number after the sum it is much higher than expected all other data are correct.
I guess your table 2 contains is not unique for column a.
Lets assume that the table 2 looks like this:
a
c
f
1
12
13
2
14
15
1
100
101
There are two rows where a is 1. Since b and f are different, the grouping does not solve this ( GROUP BY a,f,h) AS t2) and counts(*) as total is one for each row.
a
c
f
total
1
12
13
1
2
14
15
1
1
100
101
1
In the next step you join this table to your table 1. The rows of table1 with value 1 in column a are duplicated, because table2 has two entries. This lead to the fact that the sum is too high.
Instead of unnesting the tables, I recommend following approach:
-- Creating of sample data as given:
with tbl_A as (select 1 a, [struct(5 as b,2 as c),struct(3,1)] d union all select 2,[struct(2,1)] union all select null,[struct(50,51)]),
tbl_B as (select 1 as a,12 b, 13 f union all select 2,14,15 union all select 1,100,101 union all select null,500,501)
-- Query:
select *
from tbl_A A
left join
(Select a,array_agg(struct(b,f)) as B, count(1) as counts from tbl_B group by 1) B
on ifnull(A.a,-9)=ifnull(B.a,-9)

How to distinguish between real NULL and formal NULL in Postgres ROLLUP?

I am trying to determine what "level" of a ROLLUP report each given row belongs to. When initial data contains no NULLs, then it is possible just count nulls in every row (amount of null cells is the same for each given level/layer of grouping), although it also does not appear to me as a beautiful solution.
In case when initial data contains NULLs this workaround works no more: in the example query results table you see that <null> comes from real nulls, and (null) is a standard NULL placeholder for a grouped cell.
I expect to find something similar to "virtual columns as in hierarchical queries (like LEVEL, PATH, etc.).
Is there a natural way in PostgreSQL v12 to determine a level of a row in report built with ROLLUP (and in general with GROUPING SETS)?
Example of ROLLUP query result:
select a, b, sum(d.c)
from(
select null as a, 2 as b, 1 as c
union all
select null as a, 3 as b, 1 as c
union all
select 'a1' as a, 4 as b, 1 as c
union all
select 'a1' as a, 5 as b, 1 as c
union all
select 'a2' as a, 6 as b, 1 as c
) d GROUP by ROLLUP (d.a, d.b)
| a | b | sum |
--------------------------
| (null) | (null) | 5 |
| a1 | 5 | 1 |
| <null> | 2 | 1 |
| a1 | 4 | 1 |
| a2 | 6 | 1 |
| <null> | 3 | 1 |
| a2 | (null) | 1 |
| <null> | (null) | 2 |
| a1 | (null) | 2 |
--------------------------
Use Grouping, kind of
select case when grouping(a) = 1 then 'Total' else cast(a as varchar(20)) end a,
case when grouping(b) = 1 then 'Total' else cast(b as varchar(20)) end b, sum(d.c)
from(
select null as a, 2 as b, 1 as c
union all
select null as a, 3 as b, 1 as c
union all
select 'a1' as a, 4 as b, 1 as c
union all
select 'a1' as a, 5 as b, 1 as c
union all
select 'a2' as a, 6 as b, 1 as c
) d GROUP by ROLLUP (d.a, d.b)
order by grouping(a), a, grouping(b), b

Find the difference for results from two select

I have two tables:
table_1:
A | B | C
z | x | 12
z | c | 13
z | c | 10
a | s | 14
a | d | 11
table_2:
A | B | C
z | c | 10
z | x | 15
z | x | 11
a | d | 14
a | s | 12
I want to:
- group the tables by A and B
- and find the difference for SUM of C for AB.
I started with:
SELECT A, B, SUM(C) from table_1 GROUP BY A, B;
SELECT A, B, SUM(C) from table_2 GROUP BY A, B;
but I don't know how to JOIN them with adding additional column that is equal
to table_1.sum(C) - table_2.sum(c)
Expected result like:
A | B | sum1 | sum2 | diff
z | x | 12 | 26 | -14
z | c | 23 | 10 | 13
a | s | 14 | 12 | 2
a | d | 11 | 14 | -3
Use join with subquery
select X.A,X.B, sum1, sum2, sum1-sum2 as diff from
(
SELECT A, B, SUM(C) sum1
from table_1 GROUP BY A, B
)X inner join
(
SELECT A, B, SUM(C) sum2
from table_2 GROUP BY A, B
)Y on X.A=Y.A and X.B=Y.B
What do you want to happen when the groups are not the same in the two tables? inner join can be dangerous because groups will disappear.
If you want to keep all groups, then one method is union all/group by:
select a, b, sum(c1) as sum1, sum(c2) as sum2,
(sum(c2) - sum(c1)) as diff
from ((select a, b, c as c1, 0 as c2
from table_1
) union all
(select a, b, 0 as c1, c as c2
from table_2
)
) t
group by a, b

How to get most common and least common value using sql?

Input table:
a b c
1 2 1
1 2 1
1 2 2
1 3 1
1 3 3
1 3 3
2 2 5
2 2 5
2 2 7
2 3 5
2 3 8
2 3 8
Expected output:
a b min max
1 2 2 1
1 3 1 3
2 2 7 5
2 3 5 8
Logic: Group by col a and col b, get the least common and most common values from col c.
In above example for a = 1 and b = 2, the least common value for col c is 2 and most common values for col c is 1, this is depicted in the first row of the output.
Currently i am able to count the occurence of each value by using the query
select a, b, c, count(c) from table group by a, b, c
Very late answer, but I find it interesting (and fun).
You have not tagged any specific rdbms but since most databases can use CTEs:
with
counters as (
select
a, b, c, count(*) counter
from tablename
group by a, b, c
),
minmax as (
select
a, b, min(counter) mincounter, max(counter) maxcounter
from counters
group by a, b
)
select
c.a, c.b,
max(case c.counter when m.mincounter then c.c end) min,
max(case c.counter when m.maxcounter then c.c end) max
from counters c inner join minmax m
on m.a = c.a and m.b = c.b and c.counter in (m.mincounter, m.maxcounter)
group by c.a, c.b
See the demo.
Results:
| a | b | min | max |
| --- | --- | --- | --- |
| 1 | 2 | 2 | 1 |
| 1 | 3 | 1 | 3 |
| 2 | 2 | 7 | 5 |
| 2 | 3 | 5 | 8 |

How can I select each particular data up to a certain quantity?

How can I select each particular data upto a certain quantity. For example in the below table, there are 4 A, 4 B, 2 C and 1 D. Now I want to select all letters but not more than two each of it, Which will yield 2 A, 2 B, 2 C and 1 D.
+====+========+
| ID | Letter |
+====+========+
| 1 | A |
+----+--------+
| 2 | B |
+----+--------+
| 3 | B |
+----+--------+
| 4 | C |
+----+--------+
| 5 | A |
+----+--------+
| 6 | A |
+----+--------+
| 7 | C |
+----+--------+
| 8 | B |
+----+--------+
| 9 | B |
+----+--------+
| 10 | D |
+----+--------+
| 11 | A |
+----+--------+
Can anyone please help me for the above scenario?
I can think of a simple way:
select
case
when count(*) > 1
then 2
else count(*)
end,
second_column
from your_table
group by second_column;
This will give the result you want, but it won't really 'select ONLY two or less records' of each.
Using a ROW_NUMBER() function and a derived table:
CREATE TABLE myTable (id int, Letter varchar(1))
INSERT INTO myTable
VALUES (1,'A')
,(2,'B')
,(3,'B')
,(4,'C')
,(5,'A')
,(6,'A')
,(7,'C')
,(8,'B')
,(9,'B')
,(10,'D')
,(11,'A')
SELECT id, Letter
FROM
(SELECT *
,ROW_NUMBER() OVER(PARTITION BY Letter ORDER BY Letter) as rn
FROM myTable) myTable
WHERE rn = 1 or rn = 2
In essence, "cut" (PARTITION) the rows by Letters, and assign them each a number for its unique group, then pick the first two of each Letter.
Try it here:
http://rextester.com/WTKYCE51114
Use ROW_NUMBER() function to tag each record the row number and PARTITION it BY (grouping by) letter and ORDER it BY (id)
SELECT id,
letter
FROM (SELECT *,
ROW_NUMBER() OVER(PARTITION BY letter ORDER BY id) rnum
FROM myTable
) t
WHERE rnum <=2
Ordering it by id, you will have the first two instances of each letter in ascending order, thus you will have below result (note that id 1 and 5 are selected for A, 2 and 3 for B)
id letter
1 A
5 A
2 B
3 B
4 C
7 C
10 D