Error in Conditional UNION - sql

I have this SQL:
SELECT MONTH(data) AS MES, cor, CASE month(data)
WHEN 1 THEN 'Janeiro' WHEN 2 THEN 'Fevereiro' WHEN 3 THEN 'Março' WHEN 4 THEN 'Abril' WHEN 5 THEN 'Maio' WHEN 6 THEN 'Junho' WHEN 7 THEN 'Julho' WHEN
8 THEN 'Agosto' WHEN 9 THEN 'Setembro' WHEN 10 THEN 'Outubro' WHEN 11 THEN 'Novembro' WHEN 12 THEN 'Dezembro' END AS MESCOR, COUNT(*)
AS Expr1, CASE cor WHEN 'AM' THEN '2' WHEN 'VD' THEN '1' WHEN 'VM' THEN '3' END AS Expr2
FROM TBINICIATIVAS_PREVISTAS
WHERE (login ='xxxxxxx')
GROUP BY MONTH(data), cor
UNION
SELECT '1', 'VD', 'Janeiro',0,'1'
UNION
SELECT '1', 'AM', 'Janeiro',0,'2'
UNION
SELECT '1', 'VM', 'Janeiro',0,'3'
ORDER BY expr2, mes
But I need the UNION to be conditional.
Something like:
if (select count(*) .... where cond1...) = 0
UNION
SELECT '1', 'VD', 'Janeiro',0,'1'
if (select count(*) ....where cond2...) = 0
UNION
SELECT '1', 'AM', 'Janeiro',0,'2'
if (select count(*) ....where cond3...) = 0
UNION
SELECT '1', 'VM', 'Janeiro',0,'3'
I tried, but I always got a syntax error.
Is it possible to do that?

Move your conditions to a WHERE clause inside each UNION's SELECT (you can't put anything other than a SELECT between UNION).
SELECT '1', 'VD', 'Janeiro',0,'0'
UNION
SELECT '1', 'VD', 'Janeiro',0,'1'
WHERE (select count(*) ....where cond1...) = 0
UNION
SELECT '1', 'AM', 'Janeiro',0,'2'
WHERE (select count(*) ....where cond2...) = 0
UNION
SELECT '1', 'VM', 'Janeiro',0,'3'
WHERE (select count(*) ....where cond3...) = 0
Also, I note from your conditions that you are checking if the row exists, by doing a count = 0. Replacing this with an actual NOT EXISTS will be faster because the SQL engine doesn't have to actually count all rows to compare against 0, it will return immediately when a row is found.
SELECT '1', 'VD', 'Janeiro',0,'0'
UNION
SELECT '1', 'VD', 'Janeiro',0,'1'
WHERE NOT EXISTS (select 1 ....where cond1...)
UNION
SELECT '1', 'AM', 'Janeiro',0,'2'
WHERE NOT EXISTS (select 1 ....where cond2...)
UNION
SELECT '1', 'VM', 'Janeiro',0,'3'
WHERE NOT EXISTS (select 1 ....where cond3...)

Related

Recursive in Sql Server

It is my test table
with test as
(select '1'as id, '2' as id_st, '324234234' as id_dd
union all
select
'1', '2', '43252345'
union all
select
'1', '2', '43252345'
union all
select
'1', '2', '43252344'
union all
select
'1', '2', '43252344323'
union all
select
'1', '2', '2345235'
union all
select
'2', null, '324234234'
union all
select
'2', null, '3432423'
union all
select
'2', null, '43252345'
union all
select
'2', null, '3432423234523'
union all
select
'2', null, '6543456'
union all
select
'2', null, '6543456754'
union all
select
'2', null, '43252344'
)
select *
into tmp_20102022v
from test;
And I was writing code for read recursive from table tmp_20102022v
WITH CTE
AS
(
SELECT distinct
M1.id id_t,
M1.id,
m1.id_dd
FROM tmp_20102022v M1
LEFT JOIN tmp_20102022v M2
ON M1.id = M2.id_st
WHERE M2.id IS NULL
UNION ALL
SELECT
C.id,
M.id_st,
m.id_dd
FROM CTE C
JOIN tmp_20102022v M
ON C.id = M.id
)
SELECT c.id_t,c.id_dd FROM CTe c
group by c.id_t,c.id_dd
Result this code:
How I need to change my code for gain result like below.
So that the result is id = 1 which includes id_dd as well as id = 1 and 2.
And id=2 which includes only id_dd for id=2:
The recursive tree can be large.

How to apply: count(distinct ...) over (partition by ... order by) in big query?

I currently have this source table.
I am trying to get this second table from the first table, in SQL on GCP BigQuery.
My Query is the following :
SELECT
SE.MARKET_ID,
SE.LOCAL_POS_ID,
SE.BC_ID,
LEFT(SE.SALE_CREATION_DATE,6) AS DATE_ID_MONTH,
COUNT(DISTINCT
CASE
WHEN FLAG
THEN SE.CUST_ID
END)
OVER (PARTITION BY SE.MARKET_ID, SE.LOCAL_POS_ID, SE.BC_ID, LEFT(SE.SALE_CREATION_DATE,4) ORDER BY LEFT(SE.SALE_CREATION_DATE,6)) AS NB_ACTIVE_CUSTOMERS
FROM
SE
GROUP BY
SE.MARKET_ID, SE.LOCAL_POS_ID, SE.BC_ID, LEFT(SE.SALE_CREATION_DATE,6)
However, I get this error that I did not succeed to bypass :
Window ORDER BY is not allowed if DISTINCT is specified at [12:107]
I can't create a previous table with the following request :
SELECT DISTINCT
SE.MARKET_ID,
SE.LOCAL_POS_ID,
SE.BC_ID,
LEFT(SE.SALE_CREATION_DATE,6) AS DATE_ID_MONTH,
CASE
WHEN FLAG
THEN SE.CUST_ID
ELSE NULL
END AS VALID_CUST_ID
FROM
SE
in order to use a dense_rank() after that because I have 50 others indicators (and 500M rows) to add to this table (indicators based on other flags) and I can't obviously create a WITH for each of them, I need to have it in only a few WITH or none (exactly like my current query is supposed to do).
Has anyone got a clue on how I can handle that please ?
Consider below approach
select * except(ids),
array_length(array(
select distinct id
from unnest(split(ids)) id
)) as nb_active_customers,
format('%t', array(
select distinct id
from unnest(split(ids)) id
)) as distinct_values
from (
select market_id, local_pos_id, bc_id, date_id_month,
string_agg('' || ids) over(partition by market_id order by date_id_month) ids
from (
select market_id, local_pos_id, bc_id, left(sale_creation_date,6) AS date_id_month,
string_agg('' || cust_id) ids
from se
where flag = 1
group by market_id, local_pos_id, bc_id, date_id_month
)
) t
if applied to sample data in your question - output is
I think some of your sample data is incorrect but I did play with it and get a matching result, for the MPE data at least. You can accomplish this by first tagging the "distinctly counted" rows with an extra partition on CUST_ID and then first ordering on FLAG DESC. Then you would sum over that in the same way you hoped to apply count(distinct <expr>) over ...
WITH SE AS (
SELECT 1 LINE_ID, 'TW' MARKET_ID, 'X' LOCAL_POS_ID, 'MPE' BC_ID,
1 CUST_ID, '20200201' SALE_CREATION_DATE, 1 FLAG UNION ALL
SELECT 2, 'TW', 'X', 'MPE', 2, '20201005', 1 UNION ALL
SELECT 3, 'TW', 'X', 'MPE', 3, '20200415', 0 UNION ALL
SELECT 4, 'TW', 'X', 'MPE', 1, '20200223', 1 UNION ALL
SELECT 5, 'TW', 'X', 'MPE', 6, '20200217', 1 UNION ALL
SELECT 6, 'TW', 'X', 'MPE', 9, '20200715', 1 UNION ALL
SELECT 7, 'TW', 'X', 'MPE', 4, '20200223', 1 UNION ALL
SELECT 8, 'TW', 'X', 'MPE', 1, '20201008', 1 UNION ALL
SELECT 9, 'TW', 'X', 'MPE', 2, '20201019', 1 UNION ALL
SELECT 10, 'TW', 'X', 'MPE', 1, '20200516', 1 UNION ALL
SELECT 11, 'TW', 'X', 'MPE', 1, '20200129', 1 UNION ALL
SELECT 12, 'TW', 'X', 'MPE', 1, '20201007', 1 UNION ALL
SELECT 13, 'TW', 'X', 'MPE', 2, '20201005', 1 UNION ALL
SELECT 14, 'TW', 'X', 'MPE', 3, '20200505', 1 UNION ALL
SELECT 15, 'TW', 'X', 'MPE', 8, '20201103', 1 UNION ALL
SELECT 16, 'TW', 'X', 'MPE', 9, '20200820', 1
),
DATA AS (
SELECT *,
LEFT(SALE_CREATION_DATE, 6) AS SALE_MONTH,
LEFT(SALE_CREATION_DATE, 4) AS SALE_YEAR,
CASE ROW_NUMBER() OVER (
PARTITION BY MARKET_ID, LOCAL_POS_ID, BC_ID,
LEFT(SALE_CREATION_DATE, 4), CUST_ID
ORDER BY FLAG DESC, LEFT(SALE_CREATION_DATE, 6)
) WHEN 1 THEN FLAG END AS COUNTER /* assumes possible to have no flagged row */
FROM SE
)
SELECT MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_MONTH,
SUM(SUM(COUNTER)) OVER (
PARTITION BY MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_YEAR
ORDER BY SALE_MONTH
) AS NB_ACTIVE_CUSTOMERS
FROM DATA
GROUP BY MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_YEAR, SALE_MONTH
ORDER BY MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_YEAR, SALE_MONTH

Transposing and aggregating Oracle column data

I have the following data
Base End
RMSA Item 1
RMSA Item 2
RMSA Item 3
RMSB Item 1
RMSB Item 2
RMSC Item 4
I want to convert it to the following format
Key Products
RMSA;RMSB Item 1, Item 2
RMSA Item 3
RMSC Item 4
Basically, those with similar results should be grouped into 1 line. However, I can't seem to get it to work using listagg, etc since I'm grouping on two columns.
Is there any way to do this with a direct Oracle query?
You can use listagg() window analytic function twice as
with t1( Base, End ) as
(
select 'RMSA','Item 1' from dual union all
select 'RMSA','Item 2' from dual union all
select 'RMSA','Item 3' from dual union all
select 'RMSB','Item 1' from dual union all
select 'RMSB','Item 2' from dual union all
select 'RMSC','Item 4' from dual
),
t2 as
(
select
listagg(base,';') within group (order by end)
as key,
end
from t1
group by end
)
select key,
listagg(end,',') within group (order by end)
as Products
from t2
group by key
order by products;
Key Products
--------- --------------
RMSA;RMSB Item 1, Item 2
RMSA Item 3
RMSC Item 4
Demo
Below would be one way -
WITH base
AS (SELECT 'RMSA' AS base,
'Item 1' AS end1
FROM dual
UNION
SELECT 'RMSA' AS base,
'Item 2' AS end1
FROM dual
UNION
SELECT 'RMSA' AS base,
'Item 3' AS end1
FROM dual
UNION
SELECT 'RMSB' AS base,
'Item 1' AS end1
FROM dual
UNION
SELECT 'RMSB' AS base,
'Item 2' AS end1
FROM dual
UNION
SELECT 'RMSC' AS base,
'Item 4' AS end1
FROM dual),
t11
AS (SELECT t1.base base1,
t1.end1 AS end11,
t2.base base2,
t2.end1 AS end12
FROM base t1
inner join base t2
ON t1.end1 = t2.end1
WHERE t1.base > t2.base) SELECT
Concat(Concat(t11.base1, ';'), t11.base1),
Listagg(t11.end11, ',')
within GROUP (ORDER BY t11.end11)
FROM t11
GROUP BY Concat(Concat(t11.base1, ';'), t11.base1)
--above query will get you results where you have similar results
UNION
SELECT t1.base,
t1.end1
FROM base t1
left outer join t11
ON t1.base = t11.base1
AND t1.end1 = t11.end11
left outer join t11 t12
ON t1.base = t12.base2
AND t1.end1 = t12.end11
WHERE t11.base1 IS NULL
AND t12.base2 IS NULL;
Hope this helps

SQL query too complicated for me

I have a table with a note column and there can be value 'Start' or 'End'. And then there are other columns, that can have same value, but only difference is in that 'note' column...
I need to select rows which have the 'note' set to 'Start', but only those, there are no row with the same values and 'note' set to 'End'. Sorry, it's complicated to explain. I'll try to show some example.
Coll1 Coll2 Coll3 note
-----------------------------
a a a Start
a a a End
b b b Start
b b b End
c c c Start <- I need select those rows
-- There is no row with 'c c c End' combination in the table
d d d Start
d d d End
e e e Start <- I need select those rows
-- There is no row with 'e e e End' combination in the table
Can anybody help me please?
Try to use
SELECT *
FROM tbl t1
WHERE t1.note = 'Start' AND NOT EXISTS (SELECT *
FROM tbl t2
WHERE t2.note = 'End'
AND t2.Coll1 = t1.Coll1
AND t2.Coll2 = t1.Coll1
AND t2.Coll3 = t1.Coll3)
Maybe this query not optimal, but this query is easy for understand.
The most simple way should be to aggregate the records and check whether there is an End record for the group:
select col1, col2, col3
from mytable
group by col1, col2, col3
having count(case when note = 'Start' then 1 end) = 1
and count(case when note = 'End' then 1 end) = 0;
Adjust this as you like (e.g. if you are fine with several start records make it >= 1 instead of = 1).
Will this work for you?
SELECT *
FROM mytable t1
LEFT JOIN mytable t2 ON
t1.Coll1 = t2.Coll1 AND
t1.Coll2 = t2.Coll2 AND
t1.Coll3 = t2.Coll3 AND
t2.note = 'End'
WHERE t1.note = 'Start' AND t2.Coll1 IS NULL
You'll get more answers if you include CREATE TABLE and INSERT statements in your question. I'm using PostgreSQL; Oracle is similar.
create table test (
col1 char(1) not null,
col2 char(1) not null,
col3 char(1) not null,
note varchar(10) not null
check (note in ('start', 'end')),
primary key (col1, col2, col3, note)
);
I'm assuming primary key (col1, col2, col3, note). The presence of NULL complicates this approach.
insert into test values
('a', 'a', 'a', 'start'),
('a', 'a', 'a', 'end'),
('b', 'b', 'b', 'start'),
('b', 'b', 'b', 'end'),
('c', 'c', 'c', 'start'),
('d', 'd', 'd', 'start'),
('d', 'd', 'd', 'end'),
('e', 'e', 'e', 'start');
Now we can take a set of starts and a set of ends. A left outer join will preserve all the rows in starts; missing rows in ends will be filled with NULL.
with starts as (
select * from test where note = 'start'
), ends as (
select * from test where note = 'end'
)
select s.* from starts s
left outer join ends e
on e.col1 = s.col1
and e.col2 = s.col2
and e.col3 = s.col3
where e.col1 is null
and e.col2 is null
and e.col3 is null
and e.note is null;
SELECT coll1, coll2, coll3, count(*)
FROM tbl
Where note='start'
GROUP BY coll1, coll2, coll3,
HAVING count(*) < 2
Here's another solution to add to the ones you've already got:
WITH sample_data AS (SELECT 'a' Coll1, 'a' Coll2, 'a' Coll3, 'Start' note FROM dual UNION ALL
SELECT 'a' Coll1, 'a' Coll2, 'a' Coll3, 'End' note FROM dual UNION ALL
SELECT 'b' Coll1, 'b' Coll2, 'b' Coll3, 'Start' note FROM dual UNION ALL
SELECT 'b' Coll1, 'b' Coll2, 'b' Coll3, 'End' note FROM dual UNION ALL
SELECT 'c' Coll1, 'c' Coll2, 'c' Coll3, 'Start' note FROM dual UNION ALL
SELECT 'd' Coll1, 'd' Coll2, 'd' Coll3, 'Start' note FROM dual UNION ALL
SELECT 'd' Coll1, 'd' Coll2, 'd' Coll3, 'End' note FROM dual UNION ALL
SELECT 'e' Coll1, 'e' Coll2, 'e' Coll3, 'Start' note FROM dual)
-- End of mimicking a table called "sample_data" with your data in it
-- for use in the query below:
SELECT coll1,
coll2,
coll3,
MAX(CASE WHEN note = 'Start' THEN note END) note
FROM sample_data
GROUP BY coll1,
coll2,
coll3
HAVING MAX(CASE WHEN note = 'Start' THEN note END) = 'Start'
AND MAX(CASE WHEN note = 'End' THEN note END) IS NULL;
COLL1 COLL2 COLL3 NOTE
----- ----- ----- -----
e e e Start
c c c Start
It only has one access through the table (as opposed to the two accesses in Surename's answer), but you should test which solution works best with your data and table structure - one may be faster than the other.
For completeness sake, for it has been suggested in a comment to preferably think in sets:
select col1, col2, col3 from mytable where note = 'Start'
minus
select col1, col2, col3 from mytable where note = 'End';
select col1,col2,col3,count(note) from tb1
group by col1,col2,col3
having count(note)=1
also u can do it
select * from tb1
where note <> 'End' and note = 'Start'

Finding matches in multiple columns in different orders

I am trying to merge 2 databases with the same schema together, and this is one part of it.
I have changed the subject to keep it more understandable - I cannot change this schema, it's just what I'm working with.
I have a table in both my source and target databases with the following columns:
Car
CarType1
CarType2
CarType3
CarType4
I am trying to write a query that will tell me in the target database, which rows have the same Cars between the 2 databases, but different CarTypes. All I need is a count on the rows that are different.
My query written in english would be:
Bring me back a count of rows where the Car is the same and the CarTypes between the two systems do not match. It doesn't matter if the CarType is in a different CarType field between the two, just whether all of the values are contained in one of the 4 fields or not.
So if in my source database this row:
Car: Mustang
CarType1: Fast
CarType2: Convertible
CarType3: null
CarType4: null
And in my target database I have this row:
Car: Mustang
CarType1: Fast
CarType2: Convertible
CarType3: Sports
CarType4: null
This would count as a non-match, since it's a Mustang and because the aggregate of the CarType fields is different. What order the values are in does not matter for this.
How would I write this query? I cannot get a grasp on it.
;WITH SourceT AS (
SELECT 'Toyota' AS Car, 'A' AS CarType1, 'B' AS CarType2, 'C' CarType3, 'D' CarType4 UNION ALL
SELECT 'BMW' AS Car, 'A' AS CarType1, 'B' AS CarType2, 'C' CarType3, 'D' CarType4 UNION ALL
SELECT 'Mustang' AS Car, 'Fast' AS CarType1, 'Convertible' AS CarType2, 'Sports' CarType3, NULL CarType4
),
TargetT AS (
SELECT 'Toyota' AS Car, 'D' AS CarType1, 'C' AS CarType2, 'B' CarType3, 'A' CarType4 UNION ALL
SELECT 'BMW' AS Car, 'D' AS CarType1, 'C' AS CarType2, 'B' CarType3, 'A' CarType4 UNION ALL
SELECT 'Mustang' AS Car, 'Fast' AS CarType1, 'Convertible' AS CarType2, NULL CarType3, NULL CarType4 )
SELECT *
FROM SourceT s
WHERE NOT EXISTS
(
SELECT *
FROM TargetT t
WHERE s.Car = t.Car AND 0 =
(SELECT COUNT(*) FROM
( (
(SELECT s.CarType1 AS t UNION ALL
SELECT s.CarType2 AS t UNION ALL
SELECT s.CarType3 AS t UNION ALL
SELECT s.CarType4 AS t )
EXCEPT
(SELECT t.CarType1 AS t UNION ALL
SELECT t.CarType2 AS t UNION ALL
SELECT t.CarType3 AS t UNION ALL
SELECT t.CarType4 AS t )
)
UNION ALL
(
(SELECT t.CarType1 AS t UNION ALL
SELECT t.CarType2 AS t UNION ALL
SELECT t.CarType3 AS t UNION ALL
SELECT t.CarType4 AS t )
EXCEPT
(SELECT s.CarType1 AS t UNION ALL
SELECT s.CarType2 AS t UNION ALL
SELECT s.CarType3 AS t UNION ALL
SELECT s.CarType4 AS t )
)
) T
)
)
Or a slightly shorter version
SELECT *
FROM SourceT s
WHERE NOT EXISTS
(
SELECT *
FROM TargetT t
WHERE s.Car = t.Car AND
(SELECT t FROM (SELECT s.CarType1 AS t UNION ALL
SELECT s.CarType2 AS t UNION ALL
SELECT s.CarType3 AS t UNION ALL
SELECT s.CarType4 AS t ) D ORDER BY t FOR XML PATH(''))=
(SELECT t FROM (SELECT t.CarType1 AS t UNION ALL
SELECT t.CarType2 AS t UNION ALL
SELECT t.CarType3 AS t UNION ALL
SELECT t.CarType4 AS t ) D ORDER BY t FOR XML PATH(''))
)
Try this and let me know if it works:
SELECT * FROM db1.dbo.Cars
EXCEPT
SELECT c1.* FROM db1.dbo.Cars as c1
INNER JOIN db2.dbo.Cars as c2
ON c1.Car = c2.Car
AND
--Check carType1
(c1.CarType1 = c2.CarType1 OR
c1.CarType1 = c2.CarType2 OR
c1.CarType1 = c2.CarType3 OR
c1.CarType1 = c2.CarType4)
AND
--Check carType2
(c1.CarType2 = c2.CarType1 OR
c1.CarType2 = c2.CarType2 OR
c1.CarType2 = c2.CarType3 OR
c1.CarType2 = c2.CarType4)
AND
--Check carType3
(c1.CarType3 = c2.CarType1 OR
c1.CarType3 = c2.CarType2 OR
c1.CarType3 = c2.CarType3 OR
c1.CarType3 = c2.CarType4)
AND
--Check carType4
(c1.CarType4 = c2.CarType1 OR
c1.CarType4 = c2.CarType2 OR
c1.CarType4 = c2.CarType3 OR
c1.CarType4 = c2.CarType4)
NOTE:
You will have to add ISNULLs to this to either include or exclude if a column is null.
To exclude:
ISNULL(c1.CarType4, -1) = ISNULL(c2.CarType4, -2)
To include:
ISNULL(c1.CarType4, -1) = ISNULL(c2.CarType4, -1)
SELECT c1.car
FROM target.cars c1
INNER JOIN source.cars c2
ON c2.car = c1.car
WHERE
COALESCE( c1.cartype1, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
COALESCE( c1.cartype2, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
COALESCE( c1.cartype3, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
COALESCE( c1.cartype4, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
LEN( COALESCE( c1.cartype1, '') + COALESCE( c1.cartype2, '') +
COALESCE( c1.cartype3, '') + COALESCE( c1.cartype4, '') )
<>
LEN( COALESCE( c2.cartype1, '') + COALESCE( c2.cartype2, '') + COALESCE( c2.cartype3, '') +
COALESCE( c2.cartype4, '') )
;