Writing a query for multiple fields using IN - sql

SELECT a.First_Name,b.EMPID,c.Phn
FROM NAME a,Emp b,Phone c
WHERE b.Emptype = 'New'
AND a.First_name,b.Last_name,C.Phn = (SELECT a.First_Name,b.Last_name,c.Phn
FROM NAME a,Emp b,Phone c
WHERE b.Emptype = 'Old')
Basically, I want to search for new customers which have the same details (First name, Last name and Phone) as the old customers. An old customer can be converted into a new customer but its details are retained. Hence, the only thing that changes is the emptype.
Eg. (John McEnroe 47589876 Old) when converted becomes (John McEnroe 475898876 New)
[First_name,Last_name,Phone,Emptype]

Dude, here's a query that uses EXISTS.
The main difference between Exists and IN is that Exists queries using INDEX but IN goes like a lot of OR toghether.
SELECT a.First_Name
,b.EMPID
,c.Phn
FROM NAME a
,Emp b
,Phone c
WHERE b.Emptype = 'New'
AND EXISTS (Select TOP 1 1 FROM
NAME x
,Emp y
,Phone z
WHERE
x.First_Name = a.First_Name
AND y.Last_name = b.Last_name
AND z.Phn = c.Phn
AND y.Emptype = 'Old'
)
Now, looking at your query, you have 3 tables, not joined toghether (Name, Emp, Phone) so you have a real mess with the data that will come as an output. Will be, basically this formula:
(1st Table rows qty) Multiplied by (2nd Table rows qty) Multiplied by (3rd Table rows qty)
Means that if each table have 100 rows, you'll get this:
100 x 100 x 100 = 1000000 Rows in the result (Awful right?)

WITH tempTable as (
SELECT a.First_Name,b.EMPID,c.Phn
FROM NAME a,Emp b,Phone c
where b.Emptype = 'Old'
)
select a.First_Name,b.EMPID,c.Phn
FROM NAME a,Emp b,Phone c,tempTable t
where b.Emptype = 'Old' and a.First_Name = t.First_Name
AND b.Last_name = t.Last_name
AND c.Phn = t.Phn;
try this... or use subquery...

You can use multiple columns for an IN query:
AND (a.First_name,b.Last_name,C.Phn) IN (SELECT a.First_Name,b.Last_name,c.Phn
FROM ... )

Related

Fetching duplicate records based on a column value in ORACLE

So I have a customer database which has Customer_Account_ID, Phone_No and Alt_Phone_No. Now the Alt_Phone_No can have a number value or it can have blank. I want a output where the data is in below format:
Let’s say customer id is 12345678
Phone numbers are 4021234444 and 4022221234
We should have two rows on the spreadsheet:
Customer ID Phone number
12345678 4021234444
12345678 4022221234
If there is no value in Alt_Phone_No, then there shouldn't be any second entry i.e. need just the Customer ID and Phone Number in such case.
What I have roughly came up is with below:
SELECT Customer_Account_ID, Phone_No FROM Table_A a,
(SELECT Customer_Account_ID, Phone_No FROM Table_A, Table_B
where Table_A.Cust_ID = Table_B.Cust_ID
and condition = ' ' /*filter needed as per requirements*/
UNION ALL
SELECT Customer_Account_ID, Alt_Phone_No FROM Table_A, Table_B
where Table_A.Cust_ID = Table_B.Cust_ID
and condition = ' ' /*filter needed as per requirements*/ ) b
where a.Cust_ID = b.Cust_ID
and rownum < 1000 /*need rownum for testing sample data as the original data is too large*/
order by a.Cust_Id
I believe the query is incorrect. Would appreciate all the suggestions and inputs.
You can use union all:
select customerid, phonenumber
from t
union all
select customerid, altphonenumber
from t
where altphonenumber is not null;

tricky subtraction on the same column

I have a table named "prices" with 3 fields, one for country, one for the price and one for the date.
I need to make a difference between the price of each country in determined date.
i was making this code, and it works, but is making a substraction of each value on the first column, against each value on the other.
SELECT c1.value1-c2.value2 AS DIFERENCIAL
FROM (
SELECT PRICE AS value1
FROM PRICES_TABLE WHERE P = 'GERMANY'
AND (FECHA BETWEEN '17/01/2016' AND '17/01/2016')
) AS c1,
(
SELECT PRICES AS value2
FROM PRICES_TABLE WHERE P = 'PANAMA'
AND (FECHA BETWEEN '17/01/2016' AND '17/01/2016')
) AS c2
Is not what i want, if i have the values of lets say 23 values for today, and its country have assigned 23 values, i want vale1, value2.... value23 of the first column to be subtracred with value1...value23 on the second column.
Instead, is making c1.value1-c2.value1, c1.vale1-c2.value2... c1.value1-c2.value23, c1.value2-c2.value, c1,value2-c2.value2... c1.value2-c2.value23 etc.
Any idea?
If I understand correctly, you can use conditional aggregation or a join approach. So, this is one method:
SELECT ptg.fecha, ptg.hours, (ptg.prices - ptp.prices) AS DIFERENCIAL
FROM (SELECT pt.*
FROM PRICES_TABLE pt
WHERE P = 'GERMANY' AND FECHA = '2016-01-17'
) ptg JOIN
(SELECT pt.*
FROM PRICES_TABLE pt
WHERE P = 'PANAMA' AND FECHA = '2016-01-17'
) ptp
ON ptg.fecha = ptp.fecha and ptg.hours = ptp.hours;

Comparing a list of values

For example, I have a head-table with one column id and a position-table with id, head-id (reference to head-table => 1 to N), and a value. Now I select one row in the head-table, say id 1. I look into the position-table and find 2 rows which referencing to the head-table and have the values 1337 and 1338. Now I wanna select all heads which have also 2 positions with these values 1337 and 1338. The position-ids are not the same, only the values, because it is not a M to N relation. Can anyone tell me a SQL-Statement? I have no idea to get it done :/
Assuming that the value is not repeated for a given headid in the position table, and that it is never NULL, then you can do this using the following logic. Do a full outer join on the position table to the specific head positions you care about. Then check whether there is a full match.
The following query does this:
select *
from (select p.headid,
sum(case when p.value is not null then 1 else 0 end) as pmatches,
sum(case when ref.value is not null then 1 else 0 end) as refmatches
from (select p.value
from position p
where p.headid = <whatever>
) ref full outer join
position p
on p.value = ref.value and
p.headid <> ref.headid
) t
where t.pmatches = t.refmatches
If you do have NULLs in the values, you can accommodate these using coalesce. If you have duplicates, you need to specify more clearly what to do in this case.
Assuming you have:
Create table head
(
id int
)
Create table pos
(
id int,
head_id int,
value int
)
and you need to find duplicates by value, then I'd use:
Select distinct p.head_id, p1.head_id
from pos p
join pos p1 on p.value = p1.value and p.head_id<>p1.head_id
where p.head_id = 1
for specific head_id, or without last where for every head_id

How return a count(*) of 0 instead of NULL

I have this bit of code:
SELECT Project, Financial_Year, COUNT(*) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year
where it's not returning any rows when the count is zero. How do I make these rows appear with the HighRiskCount set as 0?
You can't select the values from the table when the row count is 0. Where would it get the values for the nonexistent rows?
To do this, you'll have to have another table that defines your list of valid Project and Financial_Year values. You'll then select from this table, perform a left join on your existing table, then do the grouping.
Something like this:
SELECT l.Project, l.Financial_Year, COUNT(t.Project) AS HighRiskCount
INTO #HighRisk
FROM MasterRiskList l
left join #TempRisk1 t on t.Project = l.Project and t.Financial_Year = l.Financial_Year
WHERE t.Risk_1 = 3
GROUP BY l.Project, l.Financial_Year
Wrap your SELECT Query in an ISNULL:
SELECT ISNULL((SELECT Project, Financial_Year, COUNT(*) AS hrc
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year),0) AS HighRiskCount
If your SELECT returns a number, it will pass through. If it returns NULL, the 0 will pass through.
Assuming you have your 'Project' and 'Financial_Year' where Risk_1 is different than 3, and those are the ones you intend to include.
SELECT Project, Financial_Year, SUM(CASE WHEN RISK_1 = 3 THEN 1 ELSE 0 END) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
GROUP BY Project, Financial_Year
Notice i removed the where part.
By the way, your current query is not returning null, it is returning no rows.
Use:
SELECT x.Project, x.financial_Year,
COUNT(y.*) AS HighRiskCount
INTO #HighRisk
FROM (SELECT DISTINCT t.project, t.financial_year
FROM #TempRisk1
WHERE t.Risk_1 = 3) x
LEFT JOIN #TempRisk1 y ON y.project = x.project
AND y.financial_year = x.financial_year
GROUP BY x.Project, x.Financial_Year
The only way to get zero counts is to use an OUTER join against a list of the distinct values you want to see zero counts for.
SQL generally has a problem returning the values that aren't in a table. To accomplish this (without a stored procedure, in any event), you'll need another table that contains the missing values.
Assuming you want one row per project / financial year combination, you'll need a table that contains each valid Project, Finanical_Year combination:
SELECT HR.Project, HR.Financial_Year, COUNT(HR.Risk_1) AS HighRiskCount
INTO #HighRisk HR RIGHT OUTER JOIN ProjectYears PY
ON HR.Project = PY.Project AND HR.Financial_Year = PY.Financial_Year
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY HR.Project, HR.Financial_Year
Note that we're taking advantage of the fact that COUNT() will only count non-NULL values to get a 0 COUNT result for those result set records that are made up only of data from the new ProjectYears table.
Alternatively, you might only one 0 count record to be returned per project (or maybe one per financial_year). You would modify the above solution so that the JOINed table has only that one column.
Little longer, but what about this as a solution?
IF EXISTS (
SELECT *
FROM #TempRisk1
WHERE Risk_1 = 3
)
BEGIN
SELECT Project, Financial_Year, COUNT(*) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year
END
ELSE
BEGIN
INSERT INTO #HighRisk
SELECT 'Project', 'Financial_Year', 0
END
MSDN - ISNULL function
SELECT Project, Financial_Year, ISNULL(COUNT(*), 0) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year

Selecting a single (random) row for an SQL join

I've got an sql query that selects data from several tables, but I only want to match a single(randomly selected) row from another table.
Easier to show some code, I guess ;)
Table K is (k_id, selected)
Table C is (c_id, image)
Table S is (c_id, date)
Table M is (c_id, k_id, score)
All ID-columns are primary keys, with appropriate FK constraints.
What I want, in english, is for eack row in K that has selected = 1 to get a random row from C where there exists a row in M with (K_id, C_id), where the score is higher than a given value, and where c.image is not null and there is a row in s with c_id
Something like:
select k.k_id, c.c_id, m.score
from k,c,m,s
where k.selected = 1
and m.score > some_value
and m.k_id = k.k_id
and m.c_id = c.c_id
and c.image is not null
and s.c_id = c.c_id;
The only problem is this returns all the rows in C that match the criteria - I only want one...
I can see how to do it using PL/SQL to select all relevent rows into a collection and then select a random one, but I'm stuck as to how to select a random one.
you can use the 'order by dbms_random.random' instruction with your query.
i.e.:
SELECT column FROM
(
SELECT column FROM table
ORDER BY dbms_random.value
)
WHERE rownum = 1
References:
http://awads.net/wp/2005/08/09/order-by-no-order/
http://www.petefreitag.com/item/466.cfm
with analytics:
SELECT k_id, c_id, score
FROM (SELECT k.k_id, c.c_id, m.score,
row_number() over(PARTITION BY k.k_id ORDER BY NULL) rk
FROM k, c, m, s
WHERE k.selected = 1
AND m.score > some_value
AND m.k_id = k.k_id
AND m.c_id = c.c_id
AND c.image IS NOT NULL
AND s.c_id = c.c_id)
WHERE rk = 1
This will select one row that satisfies your criteria per k_id. This will likely select the same set of rows if you run the query several times. If you want more randomness (each run produces a different set of rows), you would replace ORDER BY NULL by ORDER BY dbms_random.value
I'm not too familiar with oracle SQL, but try using LIMIT random(), if there is such a function available.