SQL - Select A only if B doesn't exist - sql

I have this SQL statement (modified, because the real query is huge):
select tblInfo.IDNum, tblAddress.PrimaryAddress
from tblInfo
join tblAddress
on tblInfo.Agent = tblAddress.Agent
where (some stuff)
And I get a table that looks roughly like this:
|| IDNum || PrimaryAddress ||
-----------------------------
|| 01234 || 1 ||
|| 23456 || 1 ||
|| abcde || 0 ||
|| abcde || 1 ||
|| zyxwv || 0 ||
I need a way to return all records that have a PrimaryAddress of 1, as well as all records that have a PrimaryAddress of 0 and don't have an IDNum already returning the PrimaryAddress of 1. i.e. In the above example, (abcde || 0) should be excluded because (abcde || 1) exists.

Use NOT EXISTS
SELECT tblInfo.IDNum, tblAddress.PrimaryAddress
FROM tblInfo
INNER JOIN tblAddress
ON tblInfo.Agent = tblAddress.Agent
WHERE tblAddress.PrimaryAddress = 1
OR ( tblAddress.PrimaryAddress = 0 AND NOT EXISTS
(
SELECT 1 FROM tblInfo t2 INNER JOIN tblAddress a2 ON t2.Agent = a2.Agent
WHERE t2.IDNum = tblInfo.IDNum AND a2.PrimaryAddress = 1
)
)

In this case, a simple GROUP BY should work for what you are trying to do. Effectively you are saying you want all IDNum values to appear once, with the PrimaryAddress value corresponding to the highest value (1 if it exists, 0 if it doesn't).
Assuming you need to preserve your original query because you're doing other work with it, you could use:
SELECT IDNum, MAX(PrimaryAddress) AS PrimaryAddress
FROM
(
select tblInfo.IDNum, tblAddress.PrimaryAddress
from tblInfo
join tblAddress
on tblInfo.Agent = tblAddress.Agent
where (some stuff)
)
GROUP BY IDNum
This should work in MS SQL Server and Oracle, not sure about other DBMSs. If the nested query doesn't work in the DBMS you're using, you should be able to populate a temporary table with the results of your first query, then perform the grouping against that table.

I hope this helps
select * from tblAddress mainTBL
where mainTBL.primaryaddress = (Case when EXISTS(select t1.IDNUM from tblAddress t1 where
mainTBL.IDNUM = t1.IDNUM AND t1.primaryAddress = 1) THEN 1 ELSE 0 END)
Check the SQL fiddle

Related

String concatenation with GROUP BY

Using Postgres, I want to convert the following records:
ID serviceaccess
11 Value1
11 Value2
11 Value3
22 Value2
22 Value1
22 Value1
Into this result:
ID
11 value1, value2, value3
22 value2, value1, value1
I can't use functions, because the system I am using doesn't support those.
I could do case statements though.
I also checked the following:
SQL Server : GROUP BY clause to get comma-separated values
I tried this, but it doesn't work:
WITH RecurRanked ( dbid, rnk, serviceaccess)
AS (
SELECT t.t1.dbid, t.t1.rnk, t.t2.serviceaccess || ', ' || t.t1.serviceaccess
FROM t.t1
INNER JOIN t.t2 ON t.t1.dbid = RecurRanked.dbid AND t.t1.rnk = RecurRanked.rnk + 1)
SELECT dbid, MAX(serviceaccess)
FROM RecurRanked
GROUP BY dbid;
SELECT t.t1.dbid, t.t1.rnk, t.t2.serviceaccess || ', ' || t.t1.serviceaccess
FROM t.t1
INNER JOIN t.t2 ON t.t1.dbid = t.t2.dbid AND t.t1.rnk = t.t2.rnk + 1
I don't fully understand what you mean by "But I can't use functions, because the system, I am using doesn't support those. I am using postgres SQL though".
You can use the string_agg aggregate function in PostgreSQL.
select ID, string_agg(serviceaccess, ',') group by ID;
It's really hard to do what you want using pure SQL. It could be useful the following (which is not the perfect solution):
(e.g.) to convert the following records:
id| serviceaccess
-----------------------
11|" "
11|"Value1"
11|"Value2"
22|" "
22|"Value1"
22|"Value2"
22|"Value3"
Tested in postgresql. Unfortunately it can not be supported on the DBMS you are using:
SELECT t1.id, (max( t1.serviceaccess || ',') ||
max( t2.serviceaccess || ',') ||
max( t3.serviceaccess || ',') ||
max( t4.serviceaccess || ',') ||
max( t5.serviceaccess)) as Services
FROM test as t1
INNER JOIN test as t2 ON t1.id =t2.id AND (t2.serviceaccess > t1.serviceaccess or
t1.serviceaccess = ' ')
INNER JOIN test as t3 ON t2.id =t3.id AND (t3.serviceaccess > t2.serviceaccess or
t2.serviceaccess = ' ')
INNER JOIN test as t4 ON t3.id =t4.id AND (t4.serviceaccess > t3.serviceaccess or
t3.serviceaccess = ' ')
INNER JOIN test as t5 ON t4.id =t5.id AND (t5.serviceaccess > t4.serviceaccess or
t4.serviceaccess = ' ')
GROUP BY t1.id
Result:
id| services
------------------------------
22| " , ,Value1,Value2,Value3"
11| " , , ,Value1,Value2"
kind regards!

Oracle sql join against extracted values

I am looking to reconcile data from 2 different tables where I need to carry out a concatenation and substr to create columns that I can use to carry out a match against.The following separate queries reflect the select statements from each table that produces the matching values that reflect sitenn.zonenn (e.g. site12.zone20) as nodename.
SELECT distinct(REGEXP_SUBSTR(B.NODE_NAME,'*site*.*')) as nodename
FROM OPC_ACT_MESSAGES A,OPC_NODE_NAMES B
WHERE A.MESSAGE_GROUP = 'Ebts_Status_Alarms'
AND A.SEVERITY <> 2
AND A.NODE_ID = B.NODE_ID;
SELECT 'site'||site_id||'.zone'||zone_id as nodename
FROM aw_active_alarms
GROUP BY site_id,zone_id;
I need to write a query that select all nodenames from one table that do not exist in the other.
Use left join to find it. It is faster than minus,not in,not exists etc.
SELECT a.nodename
FROM (SELECT DISTINCT( regexp_substr(B.node_name, '*site*.*') ) AS nodename
FROM opc_act_messages A,
opc_node_names B
WHERE A.message_group = 'Ebts_Status_Alarms'
AND A.severity <> 2
AND A.node_id = B.node_id
) a
LEFT JOIN
(SELECT 'site'
|| site_id
|| '.zone'
|| zone_id AS nodename
FROM aw_active_alarms
GROUP BY site_id,
zone_id
) b
ON a.nodename = b.nodename
WHERE b.nodename IS NULL
One simple way: use MINUS
SELECT distinct(REGEXP_SUBSTR(B.NODE_NAME,'*site*.*')) as nodename
FROM OPC_ACT_MESSAGES A,OPC_NODE_NAMES B
WHERE A.MESSAGE_GROUP = 'Ebts_Status_Alarms'
AND A.SEVERITY <> 2
AND A.NODE_ID = B.NODE_ID
MINUS
SELECT 'site'||site_id||'.zone'||zone_id as nodename
FROM aw_active_alarms
GROUP BY site_id,zone_id;
would this work?
WITH t1
AS (SELECT DISTINCT
(REGEXP_SUBSTR (B.NODE_NAME, '*site*.*')) AS nodename
FROM OPC_ACT_MESSAGES A, OPC_NODE_NAMES B
WHERE A.MESSAGE_GROUP = 'Ebts_Status_Alarms'
AND A.SEVERITY <> 2
AND A.NODE_ID = B.NODE_ID),
t2
AS ( SELECT 'site' || site_id || '.zone' || zone_id AS nodename
FROM aw_active_alarms
GROUP BY site_id, zone_id)
SELECT *
FROM t1
WHERE t1.nodename NOT IN (SELECT nodename FROM t2)

how to reference an alias in an oracle nested query?

I have a couple of nested queries like this:
(SELECT "impartidas"."idUsuarioProf"
FROM "impartidas"
WHERE "impartidas"."periodo" = "periodoPlanGrado"."periodo" and
"impartidas"."idMateria" = "materiasPlan"."idMateria") T,
(SELECT "usuarios"."apellidoPaterno" || ' , ' || "usuarios"."nombres"
FROM "usuarios"
WHERE "usuarios"."idUsuario" = 36) as "nomprofesor"
The first one outputs the teacher ID in a column named T.
What do I need to change in the second query, just so that instead of 36, it uses the value that was shown in column aliased T?
In short I need to perform the second query, based on the output ID value of the first query.
r In the absence of any context it's difficult to understand why you're taken such a convoluted approach. The obvious approach is just a straightforward join:
SELECT "impartidas"."idUsuarioProf"
, "usuarios"."apellidoPaterno" || ' , ' || "usuarios"."nombres" "nomprofesor"
FROM "impartidas"
, "periodoPlanGrado"
, "materiasPlan"
, "usuarios"
WHERE "impartidas"."periodo" = "periodoPlanGrado"."periodo"
and "impartidas"."idMateria" = "materiasPlan"."idMateria") T
and "usuarios"."idUsuario" = "impartidas"."idUsuarioProf"
/
But if you really need the inlining then you would need to do the joining externally, something like this:
select P."nomprofesor"
from
(SELECT "impartidas"."idUsuarioProf"
FROM "impartidas"
, "periodoPlanGrado"
, "materiasPlan"
WHERE "impartidas"."periodo" = "periodoPlanGrado"."periodo"
and "impartidas"."idMateria" = "materiasPlan"."idMateria") T,
(SELECT "usuarios"."apellidoPaterno" || ' , ' || "usuarios"."nombres" as "nomprofesor"
, "usuarios"."idUsuario"
FROM "usuarios" ) P
WHERE P."idUsuario" = T."idUsuarioProf"
Note that you need to include all the joining columns in the projection of each sub-query. As, you need to use an aliases to reference a derived column in the outer query.
What about this:
SELECT "usuarios"."apellidoPaterno" || ' , ' || "usuarios"."nombres" AS "nomprofesor"
FROM "usuarios"
WHERE "usuarios"."idUsuario" = (
SELECT "impartidas"."idUsuarioProf"
FROM "impartidas", "periodoPlanGrando", "materiasPlan"
WHERE "impartidas"."periodo" = "periodoPlanGrado"."periodo"
AND "impartidas"."idMateria" = "materiasPlan"."idMateria"
)
or maybe
SELECT "usuarios"."apellidoPaterno" || ' , ' || "usuarios"."nombres" AS "nomprofesor"
FROM "usuarios"
WHERE "usuarios"."idUsuario" IN (
SELECT "impartidas"."idUsuarioProf"
FROM "impartidas", "periodoPlanGrando", "materiasPlan"
WHERE "impartidas"."periodo" = "periodoPlanGrado"."periodo"
AND "impartidas"."idMateria" = "materiasPlan"."idMateria"
)
if multiple rows might be generated by the subquery (I do not know the schema and my Spanish is not very good (IS NULL) to understand what might be in the "impartidas" table).
For code maintenance and readability reasons I would write this:
SELECT u.apellidoPaterno || ' , ' || u.nombres AS nomprofesor
FROM usuarios u
WHERE u.idUsuario = (
SELECT i.idUsuarioProf
FROM impartidas i
INNER JOIN periodoPlanGrando p USING ( periodo )
INNER JOIN materiasPlan m USING ( idMateria )
-- WHERE (other condifitions)
)
or even this:
SELECT u.apellido_paterno || ' , ' || u.nombres AS nom_profesor
FROM usuarios u
WHERE u.id_usuario = (
SELECT i.id_usuario_prof
FROM impartidas i
INNER JOIN periodo_plan_grado p USING ( periodo )
INNER JOIN materias_plan m USING ( id_materia )
-- WHERE (other condifitions)
)
but this would require refractoring table and column names to be more Oracle identifier like.

How to rewrite this query (PostgreSQL) in SQL Server?

Few days ago I have asked a question about 1,2 and 3. degree connections. Question Link and #Snoopy gave an article link which can fix all my problems. Article Link
I have carefully examined this article but I was unable to use With Recursive query with SQL Server.
PostgreSQL Query:
SELECT a AS you,
b AS mightknow,
shared_connection,
CASE
WHEN (n1.feat1 = n2.feat1 AND n1.feat1 = n3.feat1) THEN 'feat1 in common'
WHEN (n1.feat2 = n2.feat2 AND n1.feat2 = n3.feat2) THEN 'feat2 in common'
ELSE 'nothing in common'
END AS reason
FROM (
WITH RECURSIVE transitive_closure(a, b, distance, path_string) AS
( SELECT a, b, 1 AS distance,
a || '.' || b || '.' AS path_string,
b AS direct_connection
FROM edges2
WHERE a = 1 -- set the starting node
UNION ALL
SELECT tc.a, e.b, tc.distance + 1,
tc.path_string || e.b || '.' AS path_string,
tc.direct_connection
FROM edges2 AS e
JOIN transitive_closure AS tc ON e.a = tc.b
WHERE tc.path_string NOT LIKE '%' || e.b || '.%'
AND tc.distance < 2
)
SELECT a,
b,
direct_connection AS shared_connection
FROM transitive_closure
WHERE distance = 2
) AS youmightknow
LEFT JOIN nodes AS n1 ON youmightknow.a = n1.id
LEFT JOIN nodes AS n2 ON youmightknow.b = n2.id
LEFT JOIN nodes AS n3 ON youmightknow.shared_connection = n3.id
WHERE (n1.feat1 = n2.feat1 AND n1.feat1 = n3.feat1)
OR (n1.feat2 = n2.feat2 AND n1.feat2 = n3.feat2);
or just
WITH RECURSIVE transitive_closure(a, b, distance, path_string) AS
( SELECT a, b, 1 AS distance,
a || '.' || b || '.' AS path_string
FROM edges
WHERE a = 1 -- source
UNION ALL
SELECT tc.a, e.b, tc.distance + 1,
tc.path_string || e.b || '.' AS path_string
FROM edges AS e
JOIN transitive_closure AS tc ON e.a = tc.b
WHERE tc.path_string NOT LIKE '%' || e.b || '.%'
)
SELECT * FROM transitive_closure
WHERE b=6 -- destination
ORDER BY a, b, distance;
As I said, I don't know how to write a recursive query with SQL Server using CTEs. Made a search and examined this page but still no luck. I couldn't run the query.
If someone interested, here is the answer;
I managed to convert the query in question to SQL by;
converting integer values to varchar(MAX). If you don't specify the length of varchar as MAX, you'll get "Types don't match between the anchor and the recursive part in column..."
I replaced || to +
I added ; to the beginning of query
Finally as #a_horse_with_no_name proposed I removed RECURSIVE from query.
Result;
;WITH transitive_closure(a, b, distance, path_string) AS
( SELECT a, b, 1 AS distance,
CAST(a as varchar(MAX)) + '.' + CAST(b as varchar(MAX)) + '.' AS path_string
FROM edges
WHERE a = 1 -- source
UNION ALL
SELECT tc.a, e.b, tc.distance + 1,
CAST(tc.path_string as varchar(MAX)) + CAST(e.b as varchar(MAX)) + '.' AS path_string
FROM edges AS e
JOIN transitive_closure AS tc ON e.a = tc.b
WHERE tc.path_string NOT LIKE '%' + CAST(e.b as varchar(MAX)) + '.%'
)
SELECT * FROM transitive_closure
WHERE b=6 -- destination
ORDER BY a, b, distance;
The recursive CTE should be the same on SQL Server (at least on a recent version, this was introduced with SQL Server 2005 if I'm not mistaken), just leave out the recursive keyword.
Note that SQL Server does not comply with the SQL standard and therefor you need to replace the || concatenation with +

Combine a 'Distinct' SQL Query with a single value query

I have an existing sql query that I'd like to apply to every record returned from a "distinct" query.
I guess something like looping through each of the returned records, storing it as a string, and using that value in the other query. How would I go about this?
sudo queries:
Select ...
for each record returned as X,
Select ... etc ... where ... LIKE X
Edit:
not sure how to make it clearer, but I know I'm probably not making it obvious. I'll try:
The distinct will return a single column, with many records. I need to apply each value to the second sql query.
So like.. Select X and Y, but Y is returned from the 2nd query I have, using X
Edit2:
If the distinct select returns
1
2
3
4
And the second query returns a single record "A" when the where clause looks like ... = '1', "B" when the where clause looks like ... = '2', "C" when the where clause looks like ... = '3', and C when the where clause looks like ... = '4'
Then I'd like my final output to look like
1 | A
2 | B
3 | C
4 | C
Edit 3:
first query:
SELECT DISTINCT [user_id] from dbo.sap_empl_subset
second query:
SELECT [name_pref_mixed]
FROM dbo.sap_empl_subset AS E
WHERE E.sap_position_no IN
(SELECT P.sap_position_no
FROM dbo.sap_position AS P
WHERE (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%[VICE ]PRESIDENT%')
OR (LTRIM(RTRIM(P.sap_position_desc)) LIKE 'CHIEF%'))
AND E.sap_org_code =
(SELECT
CASE
WHEN S.sap_org_code_level2 = 0 THEN S.sap_org_code
WHEN S.sap_org_code_level3 = 0 THEN S.sap_org_code_level1
ELSE S.sap_org_code_level2
END
FROM dbo.sap_org_structure AS S
WHERE S.sap_org_code =
(SELECT E1.sap_org_code
FROM dbo.sap_empl_subset AS E1
WHERE E1.[user_id] = '<each item from first query needs applied here>'))
SELECT *
FROM (
SELECT DISTINCT value
FROM mytable
) x
JOIN othertable y
ON y.value LIKE '%' || x.value || '%'
Update:
If you first query is
SELECT my_x
FROM mytable
WHERE my_y = '…'
and the second one is
SELECT other_z
FROM othertable
WHERE other_y = my_x
the you just need a join:
SELECT my_x, other_z
FROM mytable
JOIN othertable
ON other_y = my_x
WHERE my_y = '…'
It would be much more easy to answer if you just posted the queries.
Update 2:
Try this:
SELECT es.user_id, esp.name_pref_mixed
FROM sap_empl_subset es
JOIN sap_org_structure os
ON os.sap_org_code = es.sap_org_code
JOIN sap_empl_subset esс
ON esc.sap_org_code =
CASE
WHEN os.sap_org_code_level2 = 0 THEN os.sap_org_code
WHEN os.sap_org_code_level3 = 0 THEN os.sap_org_code_level1
ELSE os.sap_org_code_level2
END
WHERE esc.sap_position_no IN
(
SELECT sap_position_no
FROM sap_position sp
WHERE (LTRIM(RTRIM(sp.sap_position_desc)) LIKE '%[VICE ]PRESIDENT%')
OR (LTRIM(RTRIM(sp.sap_position_desc)) LIKE 'CHIEF%'))
)
DISTINCT seems to be redundant here. You have a condition in your second query:
WHERE S.sap_org_code =
(
SELECT E1.sap_org_code
FROM dbo.sap_empl_subset AS E1
WHERE E1.[user_id] = '<each item from first query needs applied here>')
)
which would throw an error if there were duplicates on sap_empl_subset.user_id
A join was not necessary to combine the two queries. All I needed was the nested select syntax as shown below, where the first line is the first query, and the first nested select is the second query. A join was not necessary.
SELECT Distinct U.[user_id] AS "User ID", (
SELECT [empl_last_name]
FROM dbo.sap_empl_subset AS E
WHERE E.sap_position_no IN
(SELECT P.sap_position_no
FROM dbo.sap_position AS P
WHERE (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%[VICE ]PRESIDENT%')
OR (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%CHIEF%')
OR (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%[EXECUTIVE ]VP%')
)
AND E.sap_org_code =
(SELECT
CASE
WHEN S.sap_org_code_level2 = 0 THEN S.sap_org_code
WHEN S.sap_org_code_level3 = 0 THEN S.sap_org_code_level1
ELSE S.sap_org_code_level2
END
FROM dbo.sap_org_structure AS S
WHERE S.sap_org_code =
(SELECT E1.sap_org_code
FROM dbo.user_id AS E1
WHERE E1.[user_id] = U.[user_id]))) As "VP"
From dbo.user_id As U WHERE U.[user_id] <> ''
ORDER BY [User ID]