How to only pull data from table 1 if table 2 also have data - sql

My problem is, I have two tables that we are using for shipping and this is what is happening. If ca.addr_1 has data is fine but if there is data in c.addr_2 then show it. And what I want is if ca.addr has the data on some line use it but if all lines are null then use c.addr.
CO.SHIP_TO_ADDR_NO AS Addr_No,
ISNULL(CA.NAME, C.NAME) AS Name,
ISNULL(CA.ADDR_1, C.ADDR_1) AS Addr_1,
ISNULL(CA.ADDR_2, C.ADDR_2) AS Addr_2,
ISNULL(CA.ADDR_3, C.ADDR_3) AS Addr_3,
ISNULL(CA.CITY, C.CITY) AS City,
ISNULL(CA.STATE, C.STATE) AS State,
ISNULL(CA.ZIPCODE, C.ZIPCODE) AS ZipCode,
ISNULL(CA.COUNTRY, C.COUNTRY) AS Country,

CASE WHEN should allow you to do what you need.
CASE WHEN ca.addr1 IS NULL AND CA.ADDR_2 IS NULL AND CA.ADDR_3 IS NULL
THEN c.addr
ELSE WHEN ....
You can add in as many variations using ELSE WHEN as you need to account for whatever permutation of address results you want to select.

Related

SELECT DISTINCT to return at most one row

Given the following db structure:
Regions
id
name
1
EU
2
US
3
SEA
Customers:
id
name
region
1
peter
1
2
henry
1
3
john
2
There is also a PL/pgSQL function in place, defined as sendShipment() which takes (among other things) a sender and a receiver customer ID.
There is a business constraint around this which requires us to verify that both sender and receiver sit in the same region - and we need to do this as part of sendShipment(). So from within this function, we need to query the customer table for both the sender and receiver ID and verify that both their region ID is identical. We will also need to ID itself for further processing down the line.
So maybe something like this:
SELECT DISTINCT region FROM customers WHERE id IN (?, ?)
The problem with this is that the result will be either an array (if the customers are not within the same region) or a single value.
Is there are more elegant way of solving this constraint? I was thinking of SELECT INTO and use a temporary table, or I could SELECT COUNT(DISTINCT region) and then do another SELECT for the actual value if the count is less than 2, but I'd like to avoid the performance hit if possible.
There is also a PL/pgSQL function in place, defined as sendShipment() which takes (among other things) a sender and a receiver customer ID.
There is a business constraint around this which requires us to verify that both sender and receiver sit in the same region - and we need to do this as part of sendShipment(). So from within this function, we need to query the customer table for both the sender and receiver ID and verify that both their region ID is identical. We will also need to ID itself for further processing down the line.
This query should work:
WITH q AS (
SELECT
COUNT( * ) AS CountCustomers,
COUNT( DISTINCT c.Region ) AS CountDistinctRegions,
-- MIN( c.Region ) AS MinRegion
FIRST_VALUE( c.Region ) OVER ( ORDER BY c.Region ) AS MinRegion
FROM
Customers AS c
WHERE
c.CustomerId = $senderCustomerId
OR
c.CustomerId = $receiverCustomerId
)
SELECT
CASE WHEN q.CountCustomers = 2 AND q.CountDistinctRegions = 2 THEN 'OK' ELSE 'BAD' END AS "Status",
CASE WHEN q.CountDistinctRegions = 2 THEN q.MinRegion END AS SingleRegion
FROM
q
The above query will always return a single row with 2 columns: Status and SingleRegion.
SQL doesn't have a "SINGLE( col )" aggregate function (i.e. a function that is NULL unless the aggregation group has a single row), but we can abuse MIN (or MAX) with a CASE WHEN COUNT() in a CTE or derived-table as an equivalent operation.
Alternatively, windowing-functions could be used, but annoyingly they don't work in GROUP BY queries despite being so similar, argh.
Once again, this is the ISO SQL committee's fault, not PostgreSQL's.
As your Region column is UUID you cannot use it with MIN, but I understand it should work with FIRST_VALUE( c.Region ) OVER ( ORDER BY c.Region ) AS MinRegion.
As for the columns:
The Status column is either 'OK' or 'BAD' based on those business-constraints you mentioned. You might want to change it to a bit column instead of a textual one, though.
The SingleRegion column will be NOT NULL (with a valid region) if CountDistinctRegions = 2 regardless of CountCustomers, but feel free to change that, just-in-case you still want that info.
For anybody else who's interested in a simple solution, I finally came up with the (kind of obvious) way to do it:
SELECT
r.region
FROM
customers s
INNER JOIN customers r ON
s.region = r.region
WHERE s.id = 'sender_id' and r.id = 'receiver_id';
Huge credit to SELECT DISTINCT to return at most one row who helped me out a lot on this and also posted a viable solution.

Postgres View: How to assign all column values to values of another column CASE WHEN?

I am trying to create a view and need to assign all values of column "category" to "metric_type" but get an error.
Table from which I get data LIN_Main_Followers
metric_name metric_type metric_value
likes function 65
shares country 101
likes SIZE_10001_OR_MORE 2
likes SIZE_11_TO_50 10
...and others
I want to create an additional column 'category' and assign 'metric_type' values to it, if it "SIZE_" like values. And assign 'company_size' value to 'metric_type' column.
My ideal result:
metric_name metric_type metric_value category
likes company_size 2 SIZE_10001_OR_MORE
likes company_size 10 SIZE_11_TO_50
VIEW code
SELECT test.metric_name,
CASE WHEN test.metric_type LIKE 'SIZE_%' THEN test.category = test.metric_type ELSE test.category END,
CASE WHEN test.metric_type LIKE 'SIZE_%' THEN test.metric_type = 'company_size' ELSE test.metric_type END,
test.metric_type,
test.metric_value
FROM(SELECT lin.metric_name,
lin.metric_type,
lin.metric_value,
'Undefined'::text AS category
FROM "LIN_Main_Followers" lin
WHERE lin.metric_type LIKE 'SIZE_%') AS test
But I get an error that
ERROR: ERROR: CASE-Types text and boolean cannot be matched
LINE 2: CASE WHEN test.metric_type LIKE 'SIZE_%' THEN test.categ...
I think you have overcomplicated the logic. You just need to select the columns you want and give them the names you want:
SELECT lin.metric_name, 'company_size' as metric_type,
lin.metric_value,
lin.metric_type as category
FROM "LIN_Main_Followers" lin
WHERE lin.metric_type LIKE 'SIZE_%'

Multiple columns in a subquery

I am trying to find the products selected in previous week vs products selected for this week to find the churn in selection decision. Currently I am doing it for a single site and the result works fine with the correct number of records. Now I want to change the code where I get the output for 10 sites in a single query.
create temporary view removes as
select distinct
asin,
lagweek,
fc,
'removed' as state,
demand_pp,
instock_pp,
source,
filter_reason,
is_missing_in_pp,
is_missing_in_dc,
is_missing_in_nmi,
asin_nmi,
asin_pre,
asin_dc,
filter_reason_old,
asin_omi,
asin_preo,
asin_dco
from sel_old so where asin not in (select asin from sel_new sn where sn.lagweek = so.lagweek);
Since this is for a single site just doing asin not in (select asin ...) works fine but now I want to look at ASINs across multiple sites from the same logic. I tried the approach below but it returns incorrect number of records.
create temporary view removes as
select distinct
so.asin,
so.lagweek,
so.fc,
'removed' as state,
so.demand_pp,
so.instock_pp,
so.source,
so.filter_reason,
so.is_missing_in_pp,
so.is_missing_in_dc,
so.is_missing_in_nmi,
so.asin_nmi,
so.asin_pre,
so.asin_dc,
so.filter_reason_old,
so.asin_omi,
so.asin_preo,
so.asin_dco
from sel_old so left join (select asin, fc, lagweek from sel_new) as sn
on (so.asin <> sn.asin)
and (so.fc = sn.fc)
and (so.lagweek = sn.lagweek);
How should I approach this. I haven't been able find an easier solution if there are any.
You can use EXISTS predicate. It doesn't produce additional records, just tests the existence of some case and filters accordingly.
select distinct
so.asin,
so.lagweek,
so.fc,
'removed' as state,
so.demand_pp,
so.instock_pp,
so.source,
so.filter_reason,
so.is_missing_in_pp,
so.is_missing_in_dc,
so.is_missing_in_nmi,
so.asin_nmi,
so.asin_pre,
so.asin_dc,
so.filter_reason_old,
so.asin_omi,
so.asin_preo,
so.asin_dco
from sel_old so
where not exists (
select 1
from sel_new sn
where so.fc = sn.fc
and so.lagweek = sn.lagweek
and so.asin = sn.asin
)

AND OR SQL operator with multiple records

I have the following query where if brand1/camp1 taken individually, query returns the correct value but if I specify more than one brand or campaigns, it returns some other number and I am not sure what the math is behind that. It is not the total of the two either.
I think it is IN operator that is specifying OR with "," as opposed to what I require it to do which is consider AND
select campaign,
sum(case when campaign in ('camp1', 'camp2') and description in ('brand1', 'brand2') then orders else 0 end) as brand_convs
from data.camp_results
where campaign in ('camp1', 'camp2') and channel='prog' and type='sbc'
group by campaign
having brand_convs > 0
order by brand_convs desc;
Any thoughts?
The problem is in the IN part as you suspected: The two IN operators do not affect eachother in any way, so campaign can be camp1 while description is brand2.
If your DBMS supports multiple columns in an IN statement, you use a single IN statement:
SELECT campaign, SUM(
CASE WHEN (campaign, description) IN (
('camp1', 'brand1'),
('camp2', 'brand2')
) THEN orders ELSE 0 END
) [rest of query...]
If not, you're probably going to have to use ANDs and ORs
SELECT campaign, SUM(
CASE WHEN
(campaign='camp1' AND description='brand1')
OR (campaign='camp2' AND description='brand2')
THEN orders ELSE 0 END
) [rest of query...]

complex MySQL Order by not working

Here is the select statement I'm using. The problem happens with the sorting. When it is like below, it only sorts by t2.userdb_user_first_name, doesn't matter if I put that first or second. When I remove that, it sorts just fine by the displayorder field value pair. So I know that part is working, but somehow the combination of the two causes the first_name to override it. What I want is for the records to be sorted by displayorder first, and then first_name within that.
SELECT t1.userdb_id
FROM default_en_userdbelements as t1
INNER JOIN default_en_userdb AS t2 ON t1.userdb_id = t2.userdb_id
WHERE t1.userdbelements_field_name = 'newproject'
AND t1.userdbelements_field_value = 'no'
AND t2.userdb_user_first_name!='Default'
ORDER BY
(t1.userdbelements_field_name = 'displayorder' AND t1.userdbelements_field_value),
t2.userdb_user_first_name;
Edit: here is what I want to accomplish. I want to list the users (that are not new projects) from the userdb table, along with the details about the users that is stored in userdbelements. And I want that to be sorted first by userdbelements.displayorder, then by userdb.first_name. I hope that makes sense? Thanks for the really quick help!
Edit: Sorry for disappearing, here is some sample data
userdbelements
userdbelements_id userdbelements_field_name userdbelements_field_value userdb_id
647 heat 1
648 displayorder 1 - Sponsored 1
645 condofees 1
userdb
userdb_id userdb_user_name userdb_emailaddress userdb_user_first_name userdb_user_last_name
10 harbourlights info#harbourlightscondosminium.ca Harbourlights 1237 Northshore Blvd, Burlington
11 harbourview info#harbourviewcondominium.ca Harbourview 415 Locust Street, Burlington
12 thebalmoral info#thebalmoralcondominium.ca The Balmoral 2075 & 2085 Amherst Heights Drive, Burlington
You are trying to use an invalid ORDER BY.
ORDER BY (t1.userdbelements_field_name = 'displayorder' AND t1.userdbelements_field_value)
It must reference a table column or returned aliased column.
I really cannot follow how this query would even be possible as you already have limited
t1.userdbelements_field_name = newproject and then you wish to order by the case of it being equal to displayorder.
Could you please modify your question to state exactly what it is that you are trying to accomplish in your order by clause?
From what I understand, you'd have to join to default_en_userdbelements solely for the displayorder value. However, I suspect there's something wrong with your query and that it probably returns duplicate values for userdb_id.
Perhaps you should say what you're trying to actually do, not explain the way you're trying to do it.
SELECT t1.userdb_id
FROM default_en_userdbelements AS t1
JOIN default_en_userdb AS t2 ON t1.userdb_id = t2.userdb_id
JOIN default_en_userdbelements AS o ON (o.userdb_id, o.userdbelements_field_name)
= (t1.userdb_id, 'displayorder')
WHERE t1.userdbelements_field_name = 'newproject'
AND t1.userdbelements_field_value = 'no'
AND t2.userdb_user_first_name != 'Default'
ORDER BY o.userdbelements_field_value,
t2.userdb_user_first_name
You could do something like this:
ORDER BY
(CASE WHEN t1.userdbelements_field_name = 'displayorder'
THEN t1.userdbelements_field_value
ELSE $some_large_number
END),
t2.userdb_user_first_name;
It sorts using the value of t1.userdbelements_field_value when t1.userdbelements_field_name = 'displayorder', but you have to supply some other value of the same type to apply for the ELSE.