Creating categories SQL - sql

I'm sure there is an easy way to do this, but I'm lost. I've tried Case and IF statements, but still nothing.
I've got two tables both holding descriptive text fields. I need to create a link that says,
IF I.desc='Door' then P.desc IN ('Door', 'Hinge','Wood Frame')
ELSE IF I.desc='Cabinet' then P.desc IN ('Door','Hinge','knob','drawer').
Not exactly what my descriptions are, but it's an example of what I need to link. Unique I.desc has to pull multiple P.desc that overlap into other unique I.desc1.
Any help would be greatly appreciated!

To me, it looks as if you needed the third table which would contain pairs of valid combinations. Something like this (Oracle-ish code; you didn't specify which database you use, but it is a simple matter to populate such a table with some data):
create table pairs (i_desc, p_desc) as
select 'Door' , 'Hinge' from dual union all
select 'Door' , 'Wood Frame' from dual union all
select 'Cabinet', 'Door' from dual union all
select 'Cabinet', 'Hinge' from dual union all
select 'Cabinet', 'Drawer' from dual;
It is easy to maintain it; if new elements appear, you'd just insert a new row, e.g.
insert into pairs (i_desc, p_desc) values ('Cabinet', 'Knob');
Query you'd then use would join these 3 tables as
select ...
from i_table i join pairs r on i.desc = r.i_desc
join p_table p on p.desc = r.p_desc
where ...

Related

Trying to pull tables from a list to insert into a loop

I'm using an Oracle database where I need to run the same query on a multitude of customer database tables all held in the same database.
The query is a select command and runs as such:
select id from customer1_table name where customer1_table.row = 1234
The problem is, instead of running the command 100 times I'm trying to figure out if I can change cusotmer1 to point to a list with all 100 customer names (they each have a unique name for the same table to denote who belongs where) in a loop statement and each successive run of the loop picks a different customer name and inserts it where needed into the select statement. Any help is GREATLY appreciated.
You can use list selection such as:
SELECT * FROM table1 WHERE ID IN (1,2,3,4,...,1001,1002,...)
Syntax may vary between database types.
You could just use a WITH clause in order to define a scope and just join it:
WITH names_list AS
(
SELECT name1 AS name FROM dual UNION ALL
SELECT name1 FROM dual UNION ALL
...
SELECT nameN FROM dual
)
SELECT name.id
FROM customer1_table name
INNER JOIN names_list nl ON name.name = nl.name;

Want to concatenate column of the second query to the first query but getting errors such as "query block has incorrect number of result columns"

SELECT
ID, PRIM_EMAIL, SEC_EMAIL, PHONE
FROM
STUDENTS.RECORDS
WHERE
ID IN (SELECT ID FROM STUDENTS.INFO WHERE ROLL_NO = '554')
UNION
SELECT NAME
FROM STUDENTS.INFO
WHERE ROLL_NO = '554';
Here Roll_No is a user inserted data so for now I have hard coded it. Basically with the help of ROLL_NO I sort the STUDENTS_INFO table from where I get the ID and based on that I try to get PRIM_EMAIL, SEC_EMAIL, PHONE from the STUDENTS.RECORDS table while matching the foreign keys of both the tables. In addition to the current result set I also want to have the prov_name column.
Any help is very much appreciated. Thank you!
I suspect that you want to put all this information on the same row, which suggests a join rather than union all:
select
r.ID,
r.PRIM_EMAIL,
r.SEC_EMAIL,
r.PHONE,
r.NAME
from STUDENTS.RECORDS r
inner join STUDENTS.INFO i ON i.ID = r.ID
where I.ROLL_NO = '554';
I think the source of your error query block has incorrect number of result columns is coming from trying to union together a table with 4 columns (id, prim_email, sec_email, phone) with 1 column (name).
From your question, I am gathering that you want a single table of id, prim_email, sec_email, phone from students.records and name from students.info.
I think the following query using CTE's might get you (partially) to your final result. You may want to refactor for optimizing performance.
with s_records as ( select * from students.records ),
s_info as ( select * from students.info ),
joined as (
select
s_records.id,
s_records.prim_email,
s_records.sec_email,
s_records.phone,
s_info.name
from s_records
left join s_info
on s_records.roll_no = s_info.roll_no
where roll_np = '554' )
select * from joined
Overall, I think that a join will be part of your solution rather than a union :-)

Oracle UNION ALL not returning full set

I have two tables that are populated with the same structure into 2 different tables: MST3_CURR and MST4_CURR. Accounts are populated into one of the two tables; each table holds accounts that are in a different 'state'. In order to generate a complete list of accounts the tables need to be fully joined and have the most current data for an account pulled.
There are several other tables which follow the exact same approach where I am using a UNION ALL operator without issue. However, for some reason with these two tables when I perform the UNION ALL I receive the record for account 4700121500023998 which is found in MST3_CURR, but the other accounts are in MST4_CURR and are not present in the final dataset. When I reverse the UNION ALL order and have MST4_CURR first followed by MST3_CURR the reverse is true.
WITH cchm_d_curr AS (
SELECT * FROM hcus_raw.cchm_d_mst3_curr
UNION ALL
SELECT * FROM hcus_raw.cchm_d_mst4_curr
)
SELECT chd_current_balance FROM cchm_d_curr
WHERE
chd_account_number IN (4700121500023998, 4700121500090430, 4700121500044101, 4700121500250492, 4700121500250013)
;
I have am at a loss to finding any kind of answer to this peculiar behaviour that Oracle 12c is exhibiting. Please let me know if there is information missing that would help answer my question.
Thank you.
It might be that order of columns in the queried tables is different, so reversing tables in the union leads to different columns to be filtered by "where", i.e.
select a, b
from (select A, b from t1
union all
select b, A from t2)
where a=1
returns something different from what expected from this query
select a, b
from (select A, b from t1
union all
select A, b from t2)
where a=1
I would check if columns order in tables hcus_raw.cchm_d_mst3_curr and hcus_raw.cchm_d_mst4_curr in the original question is same.
what does the following return [m3 first, then m4 first]??
{
WITH cchm_d_curr AS (
SELECT 'm3' src, m3.* FROM hcus_raw.cchm_d_mst3_curr m3
UNION ALL
SELECT 'm4' src, m4.* FROM hcus_raw.cchm_d_mst4_curr m4
)
SELECT src, chd_account_number, chd_current_balance FROM cchm_d_curr
WHERE
chd_account_number IN (4700121500023998, 4700121500090430, 4700121500044101, 4700121500250492, 4700121500250013)
;
}

SQL Server : compare two tables with UNION and Select * plus additional label column

I've been playing around with the sample on Jeff' Server blog to compare two tables to find the differences.
In my case the tables are a backup and the current data. I can get what I want with this SQL statement (simplified by removing most of the columns). I can then see the rows from each table that don't have an exact match and I can see from which table they come.
SELECT
MIN(TableName) as TableName
,[strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
FROM
(SELECT
'Old' as TableName
,[JAS001].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001].[dbo].[AR_CustomerAddresses].[strPostalCode]
FROM
[JAS001].[dbo].[AR_CustomerAddresses]
UNION ALL
SELECT
'New' as TableName
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strPostalCode]
FROM
[JAS001new].[dbo].[AR_CustomerAddresses]) tmp
GROUP BY
[strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
HAVING
COUNT(*) = 1
This Stack Overflow Answer gives me a much cleaner SQL query but does not tell me from which table the rows come.
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
UNION
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
EXCEPT
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
INTERSECT
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
I could use the first version but I have many tables that I need to compare and I think that there has to be an easy way to add the source table column to the second query. I've tried several things and googled to no avail. I suspect that maybe I'm just not searching for the correct thing since I'm sure it's been answered before.
Maybe I'm going down the wrong trail and there is a better way to compare the databases?
Could you use the following setup to accomplish your goal?
SELECT 'New not in Old' Descriptor, *
FROM
(
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
EXCEPT
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
) a
UNION
SELECT 'Old not in New' Descriptor, *
FROM
(
SELECT * FROM [JAS001].[dbo].[AR_CustomerAddresses]
EXCEPT
SELECT * FROM [JAS001new].[dbo].[AR_CustomerAddresses]
) b
You can't add the table name there because union, except, and intersection all compare all columns. This means you can't differentiate between them by adding the table name to the query. A group by gives you control over what columns are considered in finding duplicates so you can exclude the table name.
To help you with the large number of tables you need to compare you could write a sql query off the metadata tables that hold table names and columns and generate the sql commands dynamically off those values.
Derive one column using table names like below
SELECT MIN(TableName) as TableName
,[strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
,table_name_came
FROM
(SELECT 'Old' as TableName
,[JAS001].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001].[dbo].[AR_CustomerAddresses].[strPostalCode]
,'[JAS001].[dbo].[AR_CustomerAddresses]' as table_name_came
FROM [JAS001].[dbo].[AR_CustomerAddresses]
UNION ALL
SELECT 'New' as TableName
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCustomer]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strAddress1]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strCity]
,[JAS001new].[dbo].[AR_CustomerAddresses].[strPostalCode]
,'[JAS001new].[dbo].[AR_CustomerAddresses]' as table_name_came
FROM [JAS001new].[dbo].[AR_CustomerAddresses]
) tmp
GROUP BY [strCustomer]
,[strAddress1]
,[strCity]
,[strPostalCode]
,table_name_came
HAVING COUNT(*) = 1

Compare Items in the "IN" Clause and the resultset

I'd like to achieve something as follows, I have the following query (As simple as this),
SELECT ENT_ID,TP_ID FROM TC_LOGS WHERE ENT_ID IN (1,2,3,4,5).
Now the table TC_LOGS may not have all the items in the IN clause. So assuming that the table TC_LOGS has only 1,2. I'd like to compare the items in the IN clause i.e. 1,2,3,4,5 with 1,2(the resultset) and get a result as FOUND - 1,2 NOT FOUND - 3,4,5. I've have implemented this by applying an XSL transformation on the resultset in the application code, but I'd like to achieve this in a query, which I feel is more of an elegant solution to this problem. Also, I tried the following query with NVL, just to separate out the FOUND and NOT FOUND items as,
SELECT NVL(ENT_ID,"NOT FOUND") FROM TC_LOGS WHERE ENT_ID IN(1,2,3,4,5)
I was expecting a result as 1,2,NOT FOUND,NOT FOUND,NOT FOUND
But the above query doesn't return any result.. I'd appreciate if someone can guide me in the right path here.. Thanks much in advance.
Assuming that the items in your IN list can (or can come) from another query, you can do something like
WITH src AS (
SELECT level id
FROM dual
CONNECT BY level <= 5)
SELECT nvl(ent_id, 'Not Found' )
FROM src
LEFT OUTER JOIN tc_logs ON (src.id = tc_logs.ent_id)
In my case, the src query is just generating the numbers 1 through 5. You could just as easily fetch that data from a different table, load the numbers into a collection that you query using the TABLE operator, load the numbers into a temporary table that you query, etc. depending on how the IN list data is determined.
NVL isn't going to work because no values (including NULLS) are returned when there is no match with the IN statement.
What you can do is something like this:
SELECT NVL(ENT_ID, "NOT FOUND")
FROM TC_LOGS
RIGHT OUTER JOIN (
SELECT 1 AS 'TempID' UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5) AS Sub ON ENT_ID = TempID
The outer join will return NULLS for ENT_ID where there are no matches. Note, I'm not an Oracle person so I can't guarantee that this syntax is perfect.
if you have a table (let's use table src )contains all (1,2,3,4,5) values, you can use full join.
You can use (WITH src AS ( SELECT level id FROM dual CONNECT BY level <= 5) as the src table also)
SELECT
ent_id,tl.tp_id,src.tp_id
FROM
src
FULL JOIN
tc_logs tl
USING (ent_id)
ORDER BY
ent_id
Here is the web site for oracle full join.http://psoug.org/snippet/Oracle-PL-SQL-ANSI-Joins-FULL-JOIN_738.htm