Union with one different column - sql

I would like to use UNION to connect two views (view CSP contains 1 more column so I want to use * for 2nd in case of some items from 2nd view are not in 1st view) and that's working good but I have no duplicated configuration id either with right value and with *.
How to solve that and remove lines with '*' when there is value in csp?
SELECT csp.customer_no,
csp.contract,
csp.customer_part_no,
csp.configuration_id,
csp.catalog_no
FROM customersomething csp
UNION
SELECT spc.customer_no,
spc.contract,
spc.customer_part_no,
'*' AS "configuration_id",
spc.catalog_no
FROM
superproduct spc
+-------------+----------+-----+------------------+--------+
| customer_no | contract | ... | configuration_id | |
+-------------+----------+-----+------------------+--------+
| 17 | whatever | ... | * | view A |
| 17 | whatever | ... | right_one | view B |
+-------------+----------+-----+------------------+--------+

First, use union all unless you want to incur the overhead of removing duplicates.
Second, filter out the second one. Here is a way using not exists:
SELECT csp.customer_no, csp.contract, csp.customer_part_no,
csp.configuration_id, csp.catalog_no
FROM customersomething csp
UNION ALL
SELECT spc.customer_no, spc.contract, spc.customer_part_no,
'*' AS "configuration_id", spc.catalog_no
FROM superproduct spc
WHERE NOT EXISTS (SELECT 1
FROM customersomething csp
WHERE scp.customer_no = spc.customer_no
);

You may use this query,
SELECT spc.customer_no,
spc.contract,
spc.customer_part_no,
csp.configuration_id,
spc.catalog_no FROM superproduct spc
LEFT JOIN customersomething csp ON spc.customer_no = csp.customer_no
UNION ALL
SELECT csp.customer_no,
csp.contract,
csp.customer_part_no,
csp.configuration_id,
csp.catalog_no
FROM customersomething csp
LEFT JOIN superproduct spc ON spc.customer_no = csp.customer_no AND spc.customer_no IS NULL

Related

How to fetch data while where condition in jsonb in Postgresql

I have a table data_table like this
| id | reciever
| (bigint) |(jsonb)
----------------------------------------------------------------------
| 1 | [{"name":"ABC","email":"abc#gmail.com"},{"name":"ABDFC","email":"ab34c#gmail.com"},...]
| 2 | [{"name":"DEF","email":"deef#gmail.com"},{"name":"AFDBC","email":"a45bc#gmail.com"},...]
| 3 | [{"name":"GHI","email":"ghfi#gmail.com"},{"name":"AEEBC","email":"5gf#gmail.com"},...]
| 4 | [{"name":"LMN","email":"lfmn#gmail.com"},{"name":"EEABC","email":"gfg5#gmail.com"},...]
| 5 | [{"name":"PKL","email":"dfdf#gmail.com"},{"name":"ABREC","email":"a4rbc#gmail.com"},...]
| 6 | [{"name":"ANI","email":"fdffd#gmail.com"},{"name":"ABWC","email":"abrtc#gmail.com"},...]
when i run on pg admin it works fine
I want to fetch row by putting email in where condition like select * from data_table where receiver = 'abc#gmail.com'. there can be more data in array so i have shown "...".
I have tried like where receiver-->>'email'='abc#gmail.com' but it is working in the case {"name":"ABC","email":"abc#gmail.com"} only not in array where i have to chaeck every email in array
Help will be appreciated.
One option is to use exists and jsonb_array_elements():
select t.*
from mytable t
where exists (
select 1
from jsonb_array_elements(t.receiver) x(elt)
where x.elt ->> 'email' = 'abc#gmail.com'
)
This gives you all rows where at least one element in the array has the given email.
If you want to actually exhibit the matching elements, then you can use a lateral join instead (if more than one element in the array has the given email, this duplicates the row):
select t.*, x.elt
from mytable t
cross join lateral jsonb_array_elements(t.receiver) x(elt)
where x.elt ->> email = 'abc#gmail.com'

Best Way to Join One Column on Columns From Two Other Tables

I have a schema like the following in Oracle
Section:
+--------+----------+
| sec_ID | group_ID |
+--------+----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
+--------+----------+
Section_to_Item:
+--------+---------+
| sec_ID | item_ID |
+--------+---------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
+--------+---------+
Item:
+---------+------+
| item_ID | data |
+---------+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
+---------+------+
Item_Version:
+---------+----------+--------+
| item_ID | start_ID | end_ID |
+---------+----------+--------+
| 1 | 1 | |
| 2 | 1 | 3 |
| 3 | 2 | |
| 4 | 1 | 2 |
+---------+----------+--------+
Section_to_Item has FK into Section and Item on the *_ID columns.
Item_version is indexed on item_ID but has no FK to Item.item_ID (ran out of space in the snapshot group).
I have code that receives a list of version IDs and I want to get all items in sections in a given group that are valid for at least one of the versions passed in. If an item has no end_ID, it's valid for anything starting with start_ID. If it has an end_id, it's valid for anything up until (not including) end_ID.
What I currently have is:
SELECT Items.data
FROM Section, Section_to_Items, Item, Item_Version
WHERE Section.group_ID = 1
AND Section_to_Item.sec_ID = Section.sec_ID
AND Item.item_ID = Section_to_Item.item_ID
AND Item.item_ID = Item_Version.item_ID
AND exists (
SELECT *
FROM (
SELECT 2 AS version FROM DUAL
UNION ALL SELECT 3 AS version FROM DUAL
) passed_versions
WHERE Item_Version.start_ID <= passed_versions.version
AND (Item_Version.end_ID IS NULL or Item_Version.end_ID > passed_version.version)
)
Note that the UNION ALL statement is dynamically generated from the list of passed in versions.
This query currently does a cartesian join and is very slow.
For some reason, if I change the query to join
AND Item_Version.item_ID = Section_to_Item.item_ID
which is not a FK, the query does not do the cartesian join and is much faster.
A) Can anyone explain why this is?
B) Is this the right way to be joining this sequence of tables (I feel weird about joining Item.item_ID to two different tables)
C) Is this the right way to get versions between start_ID and end_ID?
Edit
Same query with inner join syntax:
SELECT Items.data
FROM Item
INNER JOIN Section_to_Items ON Section_to_Items.item_ID = Item.item_ID
INNER JOIN Section ON Section.sec_ID = Section_to_Items.sec_ID
INNER JOIN Item_Version ON Item_Version.item_ID = Item_.item_ID
WHERE Section.group_ID = 1
AND exists (
SELECT *
FROM (
SELECT 2 AS version FROM DUAL
UNION ALL SELECT 3 AS version FROM DUAL
) passed_versions
WHERE Item_Version.start_ID <= passed_versions.version
AND (Item_Version.end_ID IS NULL or Item_Version.end_ID > passed_version.version)
)
Note that in this case the performance difference comes from joining on Item_Version first and then joining Section_to_Item on Item_Version.item_ID.
In terms of table size, Section_to_Item, Item, and Item_Version should be similar (1000s) while Section should be small.
Edit
I just found out that apparently, the schema has no FKs. The FKs specified in the schema configuration files are ignored. They're just there for documentation. So there's no difference between joining on a FK column or not. That being said, by changing the joins into a cascade of SELECT INs, I'm able to avoid joining the entire Item table twice. I don't love the resulting query, and I don't really understand the difference, but the stats indicate it's much less work (changes the A-Rows returned from the inner most scan on Section from 656,000 to 488 (it used to be 656k starts returning 1 row, now it's 488 starts returning 1 row)).
Edit
It turned out to be stale statistics - the two queries were equivalent the whole time but with the incomplete statistics, the DB happened to notice the correct plan only in the second instance. After updating statistics, both queries generated the same plan.
I'm not sure if this is the best idea but this seems to avoid the cartesian join:
select data
from Item
where item_ID in (
select item_ID
from Item_Version
where item_ID in (
select item_ID
from Section_to_Item
where sec_ID in (
select sec_ID
from Section
where group_ID = 1
)
)
and exists (
select 1
from (
select 2 as version
from dual
union all
select 3 as version
from dual
) versions
where versions.version >= start_ID
and (end_ID is null or versions.version <)
)
)

Oracle SQL: Optimizing LEFT OUTER JOIN of two similar select statements to be smaller and/or more efficient

So I have this Oracle SQL query:
SELECT man.Toilet_Type, NVL(man.manual_PORTA_POTTY, 0) MANUAL, NVL(reg.regular_PORTA_POTTY, 0) REGULAR FROM (
SELECT A.Visitor Toilet_Type, COUNT(A.Toilet_ID) MANUAL_PORTA_POTTY FROM
BORE.EnragedPotty A,
BORE.SemiEnragedPotty B,
BORE.ManualPotty C
WHERE B.SemiEnragedPotty_ID = C.SemiEnragedPotty_ID
AND B.Toilet_ID = A.Toilet_ID
GROUP BY Visitor
ORDER BY Visitor ASC) man
LEFT OUTER JOIN
(SELECT A.Visitor Toilet_Type, COUNT(B.Toilet_ID) REGULAR_PORTA_POTTY FROM
BORE.EnragedPotty A,
BORE.RegularPotty B
WHERE B.Toilet_ID = A.Toilet_ID
GROUP BY Visitor
ORDER BY Visitor ASC) reg ON man.Toilet_Type = reg.Toilet_Type
This gives two table results. The first query, man, gives me the following output:
+===============+========+
| Toilet_Type | Manual |
+===============+========+
| Portable | 234 |
+---------------+--------+
| Home | 10 |
+---------------+--------+
| Assassination | 2 |
+---------------+--------+
The second query, reg, gives me the same output as above, but with REGULAR instead of MANUAL.
What I want to do is query the databases in a more efficient manner. I want the output to be formatted like so:
+===============+========+=========+
| Toilet_Type | Manual | Regular |
+===============+========+=========+
| Portable | 234 | 444 |
+---------------+--------+---------+
| Home | 10 | 222 |
+---------------+--------+---------+
| Assassination | 2 | 111 |
+---------------+--------+---------+
Surely this can be done in a single query without using a LEFT OUTER JOIN?
This is untested, as I didn't have any sample data, but I think something similar to this might get it done in one query:
SELECT
E.Visitor Toilet_Type,
SUM(case when SE.SemiEnragedPotty_ID is not null and
M.Toilet_ID is not null then 1 else 0 end) MANUAL_PORTA_POTTY,
SUM(case when R.Toilet_ID is not null then 1 else 0 end) REGULAR_PORTA_POTTY
FROM
BORE.EnragedPotty E,
BORE.SemiEnragedPotty SE,
BORE.ManualPotty M,
BORE.RegularPotty R
WHERE
E.SemiEnragedPotty_ID = SE.SemiEnragedPotty_ID (+) AND
E.Toilet_ID = M.Toilet_ID (+)
E.Toilet_ID = R.Toilet_ID (+)
GROUP BY Visitor
ORDER BY Visitor ASC
I may have some of the details off -- I had to rename your aliases to follow which table was which, so it wouldn't shock me if I misplaced one of them.
If you need to pull from the same dataset twice, you should consider using subquery factoring.
WITH
some_result_you_dont_want_to_repeat AS (
-- Chunk of SQL goes here
)
SELECT
-- More SQL here
FROM some_result_you_dont_want_to_repeat once
JOIN some_result_you_dont_want_to_repeat twice
ON ...
In your case, it appears that your A-B table join can be factored out.

Search an SQL table that already contains wildcards?

I have a table that contains patters for phone numbers, where x can match any digit.
+----+--------------+----------------------+
| ID | phone_number | phone_number_type_id |
+----+--------------+----------------------+
| 1 | 1234x000x | 1 |
| 2 | 87654311100x | 4 |
| 3 | x111x222x | 6 |
+----+--------------+----------------------+
Now, I might have 511132228 which will match with row 3 and it should return its type. So, it's kind of like SQL wilcards, but the other way around and I'm confused on how to achieve this.
Give this a go:
select * from my_table
where '511132228' like replace(phone_number, 'x', '_')
select *
from yourtable
where '511132228' like (replace(phone_number, 'x','_'))
Try below query:
SELECT ID,phone_number,phone_number_type_id
FROM TableName
WHERE '511132228' LIKE REPLACE(phone_number,'x','_');
Query with test data:
With TableName as
(
SELECT 3 ID, 'x111x222x' phone_number, 6 phone_number_type_id from dual
)
SELECT 'true' value_available
FROM TableName
WHERE '511132228' LIKE REPLACE(phone_number,'x','_');
The above query will return data if pattern match is available and will not return any row if no match is available.

Deleting similar columns in SQL

In PostgreSQL 8.3, let's say I have a table called widgets with the following:
id | type | count
--------------------
1 | A | 21
2 | A | 29
3 | C | 4
4 | B | 1
5 | C | 4
6 | C | 3
7 | B | 14
I want to remove duplicates based upon the type column, leaving only those with the highest count column value in the table. The final data would look like this:
id | type | count
--------------------
2 | A | 29
3 | C | 4 /* `id` for this record might be '5' depending on your query */
7 | B | 14
I feel like I'm close, but I can't seem to wrap my head around a query that works to get rid of the duplicate columns.
count is a sql reserve word so it'll have to be escaped somehow. I can't remember the syntax for doing that in Postgres off the top of my head so I just surrounded it with square braces (change it if that isn't correct). In any case, the following should theoretically work (but I didn't actually test it):
delete from widgets where id not in (
select max(w2.id) from widgets as w2 inner join
(select max(w1.[count]) as [count], type from widgets as w1 group by w1.type) as sq
on sq.[count]=w2.[count] and sq.type=w2.type group by w2.[count]
);
There is a slightly simpler answer than Asaph's, with EXISTS SQL operator :
DELETE FROM widgets AS a
WHERE EXISTS
(SELECT * FROM widgets AS b
WHERE (a.type = b.type AND b.count > a.count)
OR (b.id > a.id AND a.type = b.type AND b.count = a.count))
EXISTS operator returns TRUE if the following SQL statement returns at least one record.
According to your requirements, seems to me that this should work:
DELETE
FROM widgets
WHERE type NOT IN
(
SELECT type, MAX(count)
FROM widgets
GROUP BY type
)