What does select 2 mean in a union sql statement? - sql

I have the following code which unions two select statements but the second select starts off as select 2. What does this do?
select tax_type, sum(amount)
from bill_tax_tbl a (index multi), bill_summ_tbl b
where a.customer_no = #customer_no
and a.invoice_month = convert(tinyint,datepart(mm,dateadd(mm,-1,convert(datetime,#date))))
and a.job = b.job and a.billing_sub_job = b.billing_sub_job
and b.job_group is null
group by tax_type
union
select 2, sum(amount)
from bill_tax_tbl a (index multi), bill_summ_tbl b
where a.customer_no = #customer_no
and tax_type = 1
and a.invoice_month = convert(tinyint,datepart(mm,dateadd(mm,-1,convert(datetime,#date))))
and a.job = b.job and a.billing_sub_job = b.billing_sub_job
and b.job_group is null

2 is a value constant that will end up in the tax_type field.
Example
Given the table:
+-----+-----+
| a | b |
+===========+
| 'a' | 'b' |
| 'c' | 'd' |
+------------
Queried By:
SELECT a, b from table
UNION
SELECT 'y','z'
Would return:
+-----+-----+
| a | b |
+===========+
| 'a' | 'b' |
| 'c' | 'd' |
| 'y' | 'z' |
+------------

Related

Oracle Group and Pivot - Dynamic Pivot in Oracle

I have a table like this:
+-----+------+------------+
| SN | CASE | CASE_VALUE |
+-----+------+------------+
| A | AA | 1 |
| A | AB | 5 |
| A | AC | 3 |
| A | AD | 4 |
| B | BA | 5 |
| B | BB | 7 |
| B | BC | 5 |
| B | BD | 1 |
+-----+------+------------+
Not sure if there is any way to get
+-----+--------+--------------+--------+--------------+--------+--------------+--------+--------------+
| SN | CASE_1 | CASE_1_VALUE | CASE_2 | CASE_2_VALUE | CASE_3 | CASE_3_VALUE | CASE_4 | CASE_4_VALUE |
+-----+--------+--------------+--------+--------------+--------+--------------+--------+--------------+
| A | AA | 1 | AB | 5 | AC | 3 | AD | 4 |
| B | BA | 5 | BB | 7 | BC | 5 | BD | 1 |
+-----+--------+--------------+--------+--------------+--------+--------------+--------+--------------+
There is no order required for those four cases
28/02/2021 Edit
If there's no order between case name?
Like blow
+-----+------+------------+
| SN | CASE | CASE_VALUE |
+-----+------+------------+
| A | AB | 1 |
| A | CD | 5 |
| A | IJ | 3 |
| A | GH | 4 |
| B | OP | 5 |
| B | EF | 7 |
| B | MN | 5 |
| B | KJ | 1 |
+-----+------+------------+
One option is using conditional aggregation in order to pivot as desired such as
SELECT sn,
MAX(CASE WHEN SUBSTR(case,2,1) = 'A' THEN case END) AS case_1,
MAX(CASE WHEN SUBSTR(case,2,1) = 'A' THEN case_value END) AS case_1_value,
MAX(CASE WHEN SUBSTR(case,2,1) = 'B' THEN case END) AS case_2,
MAX(CASE WHEN SUBSTR(case,2,1) = 'B' THEN case_value END) AS case_2_value,
MAX(CASE WHEN SUBSTR(case,2,1) = 'C' THEN case END) AS case_3,
MAX(CASE WHEN SUBSTR(case,2,1) = 'C' THEN case_value END) AS case_3_value,
MAX(CASE WHEN SUBSTR(case,2,1) = 'D' THEN case END) AS case_4,
MAX(CASE WHEN SUBSTR(case,2,1) = 'D' THEN case_value END) AS case_4_value
FROM t
GROUP BY sn
Demo
The pivot is static in the above case. You can create a function returning of SYS_REFCURSOR type as below one in order to have a dynamic pivot
CREATE OR REPLACE FUNCTION Get_Pivoted_Cols RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols VARCHAR2(32767);
BEGIN
SELECT LISTAGG( 'MAX(CASE WHEN SUBSTR(case,2,1) = '''||cs||''' THEN case END ) AS "case_'||rn||'",
MAX(CASE WHEN SUBSTR(case,2,1) = '''||cs||''' THEN case_value END ) AS "case_'||rn||'_value"', ',')
WITHIN GROUP ( ORDER BY rn )
INTO v_cols
FROM ( SELECT DISTINCT SUBSTR(case,2,1) AS cs, ROW_NUMBER() OVER (PARTITION BY sn ORDER BY case) AS rn
FROM t );
v_sql :='SELECT sn,'|| v_cols ||' FROM t GROUP BY sn';
OPEN v_recordset FOR v_sql;
RETURN v_recordset;
END;
/
and call from SQL Developer's Command Line in order to see the result set
VAR rc REFCURSOR
EXEC :rc := Get_Pivoted_Cols;
PRINT rc
You need to pivot rows, then to use decode function to map your output like below. (I hope CASE is not the real name of your column).
with your_data (SN, "CASE", CASE_VALUE ) as (
select 'A', 'AA', 1 from dual union all
select 'A', 'AB', 5 from dual union all
select 'A', 'AC', 3 from dual union all
select 'A', 'AD', 4 from dual union all
select 'B', 'BA', 5 from dual union all
select 'B', 'BB', 7 from dual union all
select 'B', 'BC', 5 from dual union all
select 'B', 'BD', 1 from dual
)
select SN
, decode(SN, 'A', CASE_1, CASE_5)CASE_1
, decode(SN, 'A', CASE_1_VALUE, CASE_5_VALUE)CASE_1_VALUE
, decode(SN, 'A', CASE_2, CASE_6)CASE_2
, decode(SN, 'A', CASE_2_VALUE, CASE_6_VALUE)CASE_2_VALUE
, decode(SN, 'A', CASE_3, CASE_7)CASE_3
, decode(SN, 'A', CASE_3_VALUE, CASE_7_VALUE)CASE_3_VALUE
, decode(SN, 'A', CASE_4, CASE_8)CASE_4
, decode(SN, 'A', CASE_4_VALUE, CASE_8_VALUE)CASE_4_VALUE
from your_data t
pivot (
max(case_value) as value, max("CASE") FOR "CASE" in (
'AA' CASE_1
,'AB' CASE_2
,'AC' CASE_3
,'AD' CASE_4
,'BA' CASE_5
,'BB' CASE_6
,'BC' CASE_7
,'BD' CASE_8
)
)
;
Closest I could get, assuming (based on your data) that the first character in case will always match with sn we can pivot on that second character
select *
from (
select
data.*
, substr(case, 2, 1) case_t
from data
)
pivot (
max(case) type
, max(case_value) value
for case_t in (
'A' case_1
, 'B' case_2
, 'C' case_3
, 'D' case_4
)
)

updating records by putting sum(column) value

I have table1 like this:
+-----+-----+------+
| cat | val | type |
+-----+-----+------+
| A | 100 | c1 |
| H | 25 | c2 |
| H | 50 | c3 |
| H | 30 | c2 |
| A | 15 | c3 |
| H | 10 | c1 |
| H | 15 | c1 |
| B | 10 | c4 |
| H | 20 | c4 |
| H | 15 | c3 |
+-----+-----+------+
I need to add the sum(val) group by type to only one H belonging to each type
So I have after grouping by type we have say table2 :
+-----+-----+
| cat | val |
+-----+-----+
| c1 | 125 |
| c2 | 55 |
| c3 | 80 |
| c4 | 30 |
+-----+-----+
I need 125 added to any one H values with type c1, 55 added to any one H values with c2, and so on..If there is no H with c1,then it should create that record.
So finally we get:
+-----+-----+------+
| cat | val | type |
+-----+-----+------+
| A | 100 | c1 |
| H | 25 | c2 |
| H | 130 | c3 |
| H | 85 | c2 |
| A | 15 | c3 |
| H | 135 | c1 |
| H | 15 | c1 |
| B | 10 | c4 |
| H | 50 | c4 |
| H | 15 | c3 |
+-----+-----+------+
How do I do it without doing table1 union table2 (with 'H' as cat) group by type? Also I don't have update privileges and cannot use stored procedures. I also have to keep in mind that table1 is a result of a query involving multiple inner joins that I don't want to use over and over again for select statements.
See whether this makes sense. I've added the ID column just to display the final result in the same order as the input (for easier reading).
SQL> -- T1 is what you currently have; it can/could be your current query
SQL> with t1 (id, cat, val, type) as
2 (select 1, 'A', 100, 'C1' from dual union all
3 select 2, 'H', 25 , 'C2' from dual union all
4 select 3, 'H', 50 , 'C3' from dual union all
5 select 4, 'H', 30 , 'C2' from dual union all
6 select 5, 'A', 15 , 'C3' from dual union all
7 select 6, 'H', 10 , 'C1' from dual union all
8 select 7, 'H', 15 , 'C1' from dual union all
9 select 8, 'B', 10 , 'C4' from dual union all
10 select 9, 'H', 20 , 'C4' from dual union all
11 select 10,'H', 15 , 'C3' from dual
12 ),
13 -- sum VAL per type
14 t1_sum as
15 (select type, sum(val) sum_val
16 from t1
17 group by type
18 ),
19 -- find row number; let any H be number 1
20 t1_rn as
21 (select id, cat, val, type,
22 row_number() over (partition by type
23 order by case when cat = 'H' then 1 end) rn
24 from t1
25 )
26 -- the final result; add SUM_VAL to the first H row per type
27 select r.cat, r.val + case when r.rn = 1 then s.sum_val else 0 end val,
28 r.type
29 From t1_rn r join t1_sum s on s.type = r.type
30 order by r.id;
CAT VAL TYPE
--- ---------- ----
A 100 C1
H 80 C2
H 130 C3
H 30 C2
A 15 C3
H 135 C1
H 15 C1
B 10 C4
H 50 C4
H 15 C3
10 rows selected.
SQL>
[EDIT: trying to clarify how to use your large query]
Suppose that this is that very large and complex query of yours:
select a.cat,
case when a.cat = 'A' then b.val
when a.cat = 'Z' then c.val
else 'H'
end val,
c.type
from a join b on a.id = b.id and a.x = b.y
join c on c.id = b.idx
where a.date_column < sysdate
and c.type = 'X';
As I've said, create a view based on it as
create or replace view v_view as
select a.cat,
case when a.cat = 'A' then b.val
when a.cat = 'Z' then c.val
else 'H'
end val,
c.type
from a join b on a.id = b.id and a.x = b.y
join c on c.id = b.idx
where a.date_column < sysdate
and c.type = 'X';
and use it as a source for "my" query (from line 14 onwards):
with t1_sum as
(select type, sum(val) sum_val
from v_view --> here's the view
group by type
), etc.
Or, use the "huge" query itself as the initial CTE:
with t1 as
-- this is your "huge" query
(select a.cat,
case when a.cat = 'A' then b.val
when a.cat = 'Z' then c.val
else 'H'
end val,
c.type
from a join b on a.id = b.id and a.x = b.y
join c on c.id = b.idx
where a.date_column < sysdate
and c.type = 'X'
),
-- sum VAL per type
t1_sum as
(select type, sum(val) sum_val
from t1
group by type
), etc.

Get ID if table has one or more row exist for a condition

Suppose I have a table as below:
ID | Account| Status
---+--------+-------
1 | acct1 | A
1 | acct2 | S
1 | acct3 | C
2 | acct4 | C
2 | acct5 | C
3 | acct6 | A
3 | acct7 | C
4 | acct8 | C
4 | acct9 | C
4 | acct10 | C
Condition: return ID if accounts do not have any 'A' and 'S' status.
For this case, I only want ID '2' and '4' to be returned.
You could use HAVING and conditional SUM:
SELECT ID
FROM tab
GROUP BY ID
HAVING SUM(CASE WHEN Status IN ('A', 'S') THEN 1 ELSE 0 END) = 0
First select id which record don't have 'A' and 'S'. Then get distinct record:
Select distinct(ID) as ID
from table_name where id not in
(
select ID from table_name where status in('A', 'S')
)

How to select an attribute based on string value within a group

Table name: Copies
+------------------------------------+
| group_id | my_id | stuff |
+------------------------------------+
| 900 | 1 | Y |
| 900 | 2 | N |
| 901 | 3 | Y |
| 901 | 4 | Y |
| 902 | 5 | N |
| 902 | 6 | N |
| 903 | 7 | N |
| 903 | 8 | Y |
---------------------------------------
The output should be:
+------------------------------------+
| group_id | my_id | stuff |
+------------------------------------+
| 900 | 1 | Y |
| 903 | 8 | Y |
--------------------------------------
Hello, I have a table where I have to discern a 'good' record within a group_id based on a positive (Y) value within the stuff field. I need the full record where only one value fits this criteria. If both stuff values are Y or both are N, then they shouldn't be selected. It seems like this should be simple, but I am not sure how to proceed.
One option here is to use conditional aggregation over each group_id and retain a group if it has a mixture of yes and no answers.
WITH cte AS (
SELECT group_id
FROM Copies
GROUP BY group_id
HAVING SUM(CASE WHEN stuff = 'Y' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN stuff = 'N' THEN 1 ELSE 0 END) > 0
)
SELECT c1.*
FROM Copies c1
INNER JOIN cte c2
ON c1.group_id = c2.group_id
WHERE c1.stuff = 'Y'
One advantage of this solution is that it will show all columns of matching records.
select group_id,
min(my_id)
keep (dense_rank first order by case stuff when 'Y' then 0 end) as my_id,
'Y' as stuff
from table_1
group by group_id
having min(stuff) != max(stuff)
with rows as(
select group_id, my_id, sum(case when stuff = 'Y' then 1 else 0 end) c
from copies
group by group_id, my_id)
select c.*
from copies c inner join rows r on (c.group_id = r.group_id and c.my_id = r.my_id)
where r.c = 1;
Try this:
SELECT C.*
FROM COPIES C,
COPIES C2
WHERE C.STUFF='Y'
AND C2.STUFF='N'
AND C.GROUP_ID=C2.GROUP_ID
Try this:
SELECT t1.*
FROM copies t1
JOIN (
SELECT group_id
FROM copies
GROUP BY group_id
HAVING COUNT(CASE WHEN stuff = 'Y' THEN 1 END) = 1 AND
COUNT(CASE WHEN stuff = 'N' THEN 1 END) = 1
) t2 ON t1.group_id = t2.group_id
WHERE t1.stuff = 'Y'
This works as long as group_id values appear in couples.

Populate SQL Data

I have following data from SQL
| SR.NO | ATTR-X | ATTR-Z |
---------------------------------------
| 1 | A | a1 |
| 2 | B | a2 |
| 3 | C | a3 |
| 4 | A | a4 |
---------------------------------------
I want this to
| SR | A | B | C | ATTR-Z |
----------------------------------
| 1 | A | - | - | a1 |
| 2 | - | B | - | a2 |
| 3 | - | - | C | a3 |
| 4 | A | - | - | a4 |
----------------------------------
Can we do it in SQL queries itself?
Use a CASE statement to determine what is needed in each column.
SELECT SR_NO, CASE WHEN [Attribute -X] = 'A' THEN A ELSE NULL END AS 'A',
CASE WHEN [Attribute -X] = 'B' THEN B ELSE NULL END AS 'B',
CASE WHEN [Attribute -X] = 'C' THEN C ELSE NULL END AS 'C',
[Attribute -Z] AS 'ATTR-Z'
FROM yourtable
Could you clarify a bit further? Do you have that Data in SQL and want it populated in your HTML form?
Or
Did you mean you just want the exact same data to show up in an SQL Query
If just a simple query can do this
select 1 as SR_NO, 'A' as A,'-' as B , '-' as C, 'a1' as [ATTR-Z]
union all
select 2 as SR_NO, '-' as A,'B' as B , '-' as C, 'a2' as [ATTR-Z]
union all
select 3 as SR_NO, 'A' as A,'-' as B , '-' as C, 'a3' as [ATTR-Z]
union all
select 4 as SR_NO, '-' as A,'-' as B , 'C' as C, 'a4' as [ATTR-Z]
However this isn't really useful for anything other than displaying the table you just showed - if you want it to follow a certain pattern/cases then you'd need to let us know more about what you'd want.