Conditional IN Statement to be used inside Postgres function - sql

I am working on Postgres and I have two tables vehicles and vehicles_flag. There are no relations between the two tables and hence we can not join two tables to fetch the required data.
The table structure is below (vehicle_flag table may not contain all the id present in the vehicle table) :
[Table structure]
I am writing a function that will accept multiple input parameters. I have to select vehicle id from the vehicle_flag table only if the flag value is true: otherwise, I have to ignore the vehicel_flag table. My aim is to achieve something like this, but turns out the case statement expects scaler output:
select count(id) from vehicles
where
vehicles.id in (case
when #hasbluetooth =1 then (select distinct id from vehicle_flags where flag='bluetooth' and value = '1')
else
(select distinct id from vehicles)
end)
and
vehicles.id in (case
when #hasac =1 then (select distinct id from vehicle_flags where flag='ac' and value = '1')
else
(select distinct id from vehicles)
end)
Kindly suggest any solution to achieve this.

I suspect you want:
select v.*
from vehicle v
left join vehicle_flags vf on vf.id = v.id
group by v.id
having
(#hasbluetooth = 0 or bool_or(vf.flag = 'bluetooth' and vf.value = 1)
and (#hasac = 0 or bool_or(vf.flag = 'ac' and vf.value = 1)

Related

JOIN AND CASE MORE AN TABLE

I have 2 tables; the first one ORG contains the following columns:
ORG_REF, ARB_REF, NAME, LEVEL, START_DATE
and the second one WORK contains these columns:
ARB_REF, WORK_STREET - WORK_NUM, WORK_ZIP
I want to do the following: write a select query that search in work and see if the WORK_STREET, WORK_ZIP are duplicate together, then you should look at WORK_NUM. If it is the same then output value ' ok ', but if WORK_NUM is not the same, output 'not ok'
I wrote this SQL query:
select
A.ARB_REF, A.WORK_STREET, A.WORK_NUM, A.WORK_ZIP
case when B.B = 1 then 'OK' else 'not ok' end
from
work A
join
(select
WORK_STREET, WORK_ZIP count(distinct , A.WORK_NUM) B
from
WORK
group by
WORK_STREET, WORK_ZIP) B on B.WORK_STREET = A.WORK_STREET
and B.WORK_ZIP = A.WORK_ZIP
Now I want to join the table ORG with this result I want to check if every address belong to org if it belong I should create a new column result and set it to yes in it (RESULT) AND show the "name" column otherwise set no in 'RESULT'.
Can anyone help me please?
While you can accomplish your result by adding a left outer join to the query you've already started, it might be easiest to just use count() over....
with org_data as (
-- do the inner join before the left join later
select * from org1 o1 inner join org2 o2 on o2.orgid = o1.orgid
)
select
*,
count(*) over (partition by WORK_STREET, WORKZIP) as cnt,
case when o.ARB_REF is not null then 'Yes' else 'No' end as result
from
WORK w left outer join org_data o on o.ARB_REF = w.ARB_REF

Select rows having value combination listed in another table

I have tables:
Result containing 5 columns: result_id, num_1, num_2, num_3, num_4
Ref containing 4 columns: num_1, num_2, num_3, num_4
Columns num contain random int in range of 1-9
Aim of exercise is to display all result_id from Result table which have num values combination present in Ref table and to display result_id which have not met combination criteria.
I've been trying left joining ref to result, but unfortunately no success. Could you please share some light how to deal with it?
If you want the result_id for which combination exists in the ref table then use following JOIN query:
select distinct r.result_id
from results r
join ref on r.num_1 = ref.num_1 and r.num_2 = ref.num_2
and r.num_3 = ref.num_3 and r.num_4 = ref.num_4
If you want the result_id for which combination do not exists in REF table then use the LEFT JOIN as follows:
select r.result_id
from results r
left join ref on r.num_1 = ref.num_1 and r.num_2 = ref.num_2
and r.num_3 = ref.num_3 and r.num_4 = ref.num_4
where ref.num_1 is null -- or use PK / Not nullable column of REF table here
Assuming you want the columns to "line up" and you want to add a flag to the result_id in the first table, then use exists:
select t1.*,
(case when exists (select 1
from table2 t2
where t2.n1 = t1.n1 and t2.n2 = t1.n2 and t2.n3 = t1.n3 and t2.n4
)
then 'present' else 'not present'
end) as flag
from t2;

what is wrong with my sql query (case when exists)

I'm trying to understand how case when exists expression works, there is two tables one is trOrderHeader which stores main info about any order. Other is trOrderLine which stores details about an order and there is a column IsClosed which indicates Order is somehow closed (canceled or completed).
So my query is below which I am trying to get OrderLineID with IsClosed column (I can do it by joins it is easy but I am trying case when exists expression) but all IsClosed column returns 1;
SELECT
OrderLineId,
IsClosed =
CASE
WHEN EXISTS (select * from trOrderHeader where IsClosed=1)
THEN 1
WHEN EXISTS (select * from trOrderHeader where IsClosed=0)
THEN 0
END
FROM
trOrderLine
GROUP BY OrderLineId
Isn't this supposed to give me if an Order is Closed write 1, if an Order is not Closed write 0?
Did I misunderstand case when exists expression?
You would need to correlate the subqueries. Assuming that column OrderId can be used to relate the tables, then:
SELECT
OrderLineId,
IsClosed =
CASE
WHEN EXISTS (select 1 from trOrderHeader h where h.IsClosed = 1 AND h.OrderId = l.OrderId)
THEN 1
WHEN EXISTS (select 1 from trOrderHeader h where IsClosed = 0 AND h.OrderId = l.OrderId)
THEN 0
END
FROM trOrderLine l
GROUP BY OrderLineId
However, the logic of the query could probably be simplified by using a JOIN and an aggregate function to compute the status. Something like this could be what you need:
SELECT l.OrderLineId, MAX(h.isClosed) IsClosed
FROM trOrderLine l
INNER JOIN trOrderHeader h ON h.OrderId = l.OrderId
GROUP BY l.OrderLineId

How can I get a distinct count of a named inner join?

I've built a query for a summary table of information, and it's almost there, with one small bug. The confirmed_class_count variable comes back too high if there's multiple users on a class, leading me to believe that the number isn't distinct
Here's my current code:
SELECT "staffs".*,
count(distinct subclasses) as class_count,
sum(case when users.confirmed_at is not null then 1 else 0 end) confirmed_class_count
FROM
staffs
INNER JOIN classes as subclasses on staffs.staff_id = ANY(subclasses.staff)
INNER JOIN "classes_users" ON "classes_users"."class_id" = "subclasses"."id"
INNER JOIN "users" ON "users"."id" = "classes_users"."user_id"
INNER JOIN class_types ON class_types.code = subclasses.class_type_code
WHERE
(subclasses.closed_date is NULL OR subclasses.closed_date > '2019-09-06')
GROUP BY
staffs.id ORDER BY "staffs"."full_name" ASC
I want to replace the sum with something like (select distinct count(*) from subcases where users.confirmed_at is not null) as confirmed_case_count but I get relation "subclasses" does not exist.
How do I get what I'm intending here?
You can use count distinct with conditional aggregation. Replace
sum(class when users.confirmed_at is not null then 1 else 0 end) confirmed_class_count
^ looks like a typo, this should be case not class
with
count(distinct case when users.confirmed_at is not null then classes_users.class_id end) confirmed_class_count

SQL Elaborate Joins Query

I'm trying to solve the below problem.
I feel like it is possible, but I can't seem to get it.
Here's the scenario:
Table 1 (Assets)
1 Asset-A
2 Asset-B
3 Asset-C
4 Asset-D
Table 2 (Attributes)
1 Asset-A Red
2 Asset-A Hard
3 Asset-B Red
4 Asset-B Hard
5 Asset-B Heavy
6 Asset-C Blue
7 Asset-C Hard
If I am looking for something having the same attributes as Asset-A, then it should identify Asset-B since Asset-B has all the same attributes as Asset-A (it should discard heavy, since Asset-A didn't specify anything different or the similar). Also, if I wanted the attributes for only Asset-A AND Asset-B that were common, how would I get that?
Seems simple, but I can't nail it...
The actual table I am using, is almost precisely Table2, simply an association of an AssetId, and an AttributeId so:
PK: Id
int: AssetId
int: AttributeId
I only included the idea of the asset table to simplify the question.
SELECT ato.id, ato.value
FROM (
SELECT id
FROM assets a
WHERE NOT EXISTS
(
SELECT NULL
FROM attributes ata
LEFT JOIN
attributes ato
ON ato.id = ata.id
AND ato.value = ata.value
WHERE ata.id = 1
AND ato.id IS NULL
)
) ao
JOIN attributes ato
ON ato.id = ao.id
JOIN attributes ata
ON ata.id = 1
AND ata.value = ato.value
, or in SQL Server 2005 (with sample data to check):
WITH assets AS
(
SELECT 1 AS id, 'A' AS name
UNION ALL
SELECT 2 AS id, 'B' AS name
UNION ALL
SELECT 3 AS id, 'C' AS name
UNION ALL
SELECT 4 AS id, 'D' AS name
),
attributes AS
(
SELECT 1 AS id, 'Red' AS value
UNION ALL
SELECT 1 AS id, 'Hard' AS value
UNION ALL
SELECT 2 AS id, 'Red' AS value
UNION ALL
SELECT 2 AS id, 'Hard' AS value
UNION ALL
SELECT 2 AS id, 'Heavy' AS value
UNION ALL
SELECT 3 AS id, 'Blue' AS value
UNION ALL
SELECT 3 AS id, 'Hard' AS value
)
SELECT ato.id, ato.value
FROM (
SELECT id
FROM assets a
WHERE a.id <> 1
AND NOT EXISTS
(
SELECT ata.value
FROM attributes ata
WHERE ata.id = 1
EXCEPT
SELECT ato.value
FROM attributes ato
WHERE ato.id = a.id
)
) ao
JOIN attributes ato
ON ato.id = ao.id
JOIN attributes ata
ON ata.id = 1
AND ata.value = ato.value
I don't completely understand the first part of your question, identifying assets based on their attributes.
Making some assumptions about column names, the following query would yield the common attributes between Asset-A and Asset-B:
SELECT [Table 2].Name
FROM [Table 2]
JOIN [Table 1] a ON a.ID = [Table 2].AssetID AND a.Name = 'Asset-A'
JOIN [Table 1] b ON b.ID = [Table 2].AssetID AND b.Name = 'Asset-B'
GROUP BY [Table 2].Name
Select * From Assets A
Where Exists
(Select * From Assets
Where AssetId <> A.AssetID
And (Select Count(*)
From Attributes At1 Join Attributes At2
On At1.AssetId <> At2.AssetId
And At1.attribute <> At2.Attribute
Where At1.AssetId = A.AssetId Asset) = 0 )
And AssetId = 'Asset-A'
select at2.asset, count(*)
from attribute at1
inner join attribute at2 on at1.value = at2.value
where at1.asset = "Asset-A"
and at2.asset != "Asset-A"
group by at2.asset
having count(*) = (select count(*) from attribute where asset = "Asset-A");
Find all assets who have every attribute that "A" has (but also may have additional attributes):
SELECT Other.ID
FROM Assets Other
WHERE
Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself
AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE
AttA.AssetID='Asset-A'
AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE
AttOther.AssetID=Other.ID AND AttOther.AttributeID = AttA.AttributeID
)
)
I.e., "find any asset where there is no attribute of A that is not also an attribute of this asset".
Find all assets who have exactly the same attributes as "A":
SELECT Other.ID
FROM Assets Other
WHERE
Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself
AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE
AttA.AssetID='Asset-A'
AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE
AttOther.AssetID=Other.ID
AND AttOther.AttributeID = AttA.AttributeID
)
)
AND NOT EXISTS (SELECT NULL FROM Attributes AttaOther WHERE
AttaOther.AssetID=Other.ID
AND NOT EXISTS (SELECT NULL FROM Attributes AttaA WHERE
AttaA.AssetID='Asset-A'
AND AttaA.AttributeID = AttaOther.AttributeID
)
)
I.e., "find any asset where there is no attribute of A that is not also an attribute of this asset, and where there is no attribute of this asset that is not also an attribute of A."
This solution works as prescribed, thanks for the input.
WITH Atts AS
(
SELECT
DISTINCT
at1.[Attribute]
FROM
Attribute at1
WHERE
at1.[Asset] = 'Asset-A'
)
SELECT
DISTINCT
Asset,
(
SELECT
COUNT(ta2.[Attribute])
FROM
Attribute ta2
INNER JOIN
Atts b
ON
b.[Attribute] = ta2.[attribute]
WHERE
ta2.[Asset] = ta.Asset
)
AS [Count]
FROM
Atts a
INNER JOIN
Attribute ta
ON
a.[Attribute] = ta.[Attribute]
Find all assets that have all the same attributes as asset-a:
select att2.Asset from attribute att1
inner join attribute att2 on att2.Attribute = att1.Attribute and att1.Asset <> att2.Asset
where att1.Asset = 'Asset-A'
group by att2.Asset, att1.Asset
having COUNT(*) = (select COUNT(*) from attribute where Asset=att1.Asset)
I thought maybe I can do this with LINQ and then work my way backwards with:
var result = from productsNotA in DevProducts
where productsNotA.Product != "A" &&
(
from productsA in DevProducts
where productsA.Product == "A"
select productsA.Attribute
).Except
(
from productOther in DevProducts
where productOther.Product == productsNotA.Product
select productOther.Attribute
).Single() == null
select new {productsNotA.Product};
result.Distinct()
I thought that translating this back to SQL with LinqPad would result into a pretty SQL query. However it didn't :). DevProducts is my testtable with a column Product and Attribute. I thought I'd post the LINQ query anyways, might be useful to people who are playing around with LINQ.
If you can optimize the LINQ query above, please let me know (it might result in better SQL ;))
I'm using following DDL
CREATE TABLE Attributes (
Asset VARCHAR(100)
, Name VARCHAR(100)
, UNIQUE(Asset, Name)
)
Second question is easy
SELECT Name
FROM Attributes
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A')
AND Asset = 'B'
First question is not more difficult
SELECT Asset
FROM Attributes
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A')
GROUP BY Asset
HAVING COUNT(*) = (SELECT COUNT(*) FROM FROM Attributes WHERE Asset = 'A')
Edit:
I left AND Asset != 'A' out of the WHERE clause of the second snippet for brevity