MySQL Multi level nested CASE WHEN THEN ELSE with priority? - mysql

I am trying to wrap my mind around this little problem I've run into.
I am joining multiple tables containing vehicle information (so make, model, type, subtype, part numbers and part titles) in order to go through my "title override" table that might (or might not) contain entries where certain vehicle parts would have their title overwritten in certain cases, for certain vehicle makes, models, types or subtypes.
As in, think of using standard oil for all BMW cars, and then for BMW M5 there is an exception that overrides that standard oil with another premium oil. Or say standard oil is being used for all cars UNLESS otherwise specified in this table. And the priority goes down the list:
specific model (highest priority)
specific make (2nd highest priority)
subtype (3rd highest priority)
So the override table ("ov") looks something like this:
id | type_id | make_id | subtype_id | model_id | part_id | part_override
1 | 2 | 0 | 0 | 0 | 33 | Global-Replacement-Oil
2 | 2 | 1 | 0 | 0 | 33 | Yamaha-Premium-Oil
3 | 2 | 0 | 2 | 0 | 33 | Global-Dirtbike-Oil
4 | 2 | 1 | 4 | 0 | 33 | Yamaha-Streetbike-Oil
5 | 2 | 0 | 0 | 199 | 33 | Yamaha-R6-2015-Oil
** type_id = 2 is for motorcycles, type_id = 3 is for cars, for example
** if make_id, subtype_id or model_id are = 0 that means that override should apply to all makes, subtypes and models, unless value <> 0 is specified.
And for that part_id = 33, I only need to display it once in my results, not multiple times, depending on which model is queried in the application and where it fits in that "matrix".
So if I am running this query on Yamaha R6 2015 edition and need to pull the code/title for that part_id=33 for that model, "model_id" column has the highest priority over all the other ones, so I'll ignore all the other ones and show title for record #5.
If I however pick another Yamaha Street bike, the highest priority match I'd get would be record #4 (as it's got both make_id and subtype_id defined).
Or if I pick a random Dirtbike, I'll get a match on record #3 (on subtype_id), so that code/title will display instead.
And then if I pick any Yamaha bike, I'll get a match on record #2 (on make_id).
And at last, if none of those are specified and I just pick a random bike, it will show record #1 instead, as that will make a match on type_id alone (bikes = 2).
This is the query that I have so far (or part of it):
SELECT DISTINCT
CASE
WHEN p.part_id = '72' AND ov.part_override != "" AND ov.type_id = p.type_id AND
(CASE
WHEN ov.model_id IS NOT NULL
THEN ov.model_id = "$MODEL_ID"
ELSE
CASE
WHEN ov.make_id != 0
THEN ov.make_id = m.make_id
ELSE
CASE
WHEN ov.subtype_id != 0
THEN ov.subtype_id = st.subtype_id
ELSE 1
END
END
END)
THEN ov.part_override
ELSE p.part_number
END part_number,
ov.make_id,
....
FROM parts p ON ...
INNER JOIN makes m ON p.make_id = m.make_id
INNER JOIN ...
(just joining a bunch of other separate tables that contain all types,
subtypes, models and so on, respectively - irrelevant for the logic above)
What I am getting with this query is all the parts that apply to a specific model listed, so if I run it for a specific model like Yamaha R6 2015 I'll get the records #1,2 and 5 come up. But I just need record #5 to show up, as explained above, in the order of importance and priority.
If I do a GROUP BY part_id or something like that then I only get one record showing, but it's not necessarily the right one, in terms of highest priority.
What am I missing?
Or how can I cascade this whole importance/priority check down that query, so that while going through all the parts records in other joined tables and filtering it based on this override ("ov") table it only spits out the highest priority record, based on some sort of a waterfall or cascade rule?
Not sure how to write that, or if it's even possible.
Or if I have to run it as a recurring stored procedure of sorts, that's fine too.
Thanks!

Without seeing more accurately your other table joins, nor the actual table name you have here, you might want something with multiple left-joins to the override table, then pull COALESCE based on the first FOUND value. Something like..
select
...
coalesce( P_OV.part_override, M_OV.part_override,
MOD_OV.part_override, P.PartName ) as FinalPartName
from
Parts P
left join PartsOverride P_OV
on p.id = P_OV.part_id
join make m
on p.make_id = m.make_id
left join PartsOverride M_OV
on m.make_id = M_OV.make_id
join model mo
on p.model_id = mo.model_id
left join PartsOverride MOD_OV
on m.make_id = MOD_OV.make_id
Then, you can change the order of priority of which "OV" version you want within the coalesce(). So if Model is higher priority than make:
coalesce( MOD_OV.part_override, M_OV.part_override,
P_OV.part_override, P.PartName ) as FinalPartName
Whichever field is NOT NULL FIRST WINS.

Related

SQL Validate a column with the same column

I have the following situation. I have a table with all info of article. I will like to compare the same column with it self. because I have multiple type of article. Single product and Master product. the only way that I have to differences it, is by SKU. for example.
ID | SKU
1 | 11111
2 | 11112
3 | 11113
4 | 11113-5
5 | 11113-8
6 | 11114
7 | 11115
8 | 11115-1-W
9 | 11115-2
10 | 11116
I only want to list or / and count only the sku that are full unique. follow th example the sku that are unique and no have variant are (ID = 1, 2, 6 and 10) I will want to create a query where if 11113 are again on the column not cout it. so in total I will be 4 unique sku and not "6 (on total)". Please let me know. if this are possible.
Assuming the length of master SKUs are 5 characters, try this:
select a.*
from mytable a
left join mytable b on b.sku like concat(a.sku, '%')
where length(a.sku) = 5
and b.sku is null
This query joins master SKUs to child ones, but filters out successful joins - leaving only solitary master SKUs.
You can do this by grouping and counting the unique rows.
First, we will need to take your table and add a new column, MasterSKU. This will be the first five characters of the SKU column. Once we have the MasterSKU, we can then GROUP BY it. This will bundle together all of the rows having the same MasterSKU. Once we are grouping we get access to aggregate functions like COUNT(). We will use that function to count the number of rows for each MasterSKU. Then, we will filter out any rows that have a COUNT() over 1. That will leave you with only the unique rows remaining.
Take that unique list and LEFT JOIN it back into your original table to grab the IDs.
SELECT ID, A.MasterSKU
FROM (
SELECT
MasterSKU = SUBSTRING(SKU,1,5),
MasterSKUCount = COUNT(*)
FROM MyTable
GROUP BY SUBSTRING(SKU,1,5)
HAVING COUNT(*) = 1
) AS A
LEFT JOIN (
SELECT
ID,
MasterSKU = SUBSTRING(SKU,1,5)
FROM MyTable
) AS B
ON A.MasterSKU = B.MasterSKU
Now one thing I noticed from you example. The original SKU column really looks like three columns in one. We have multiple values being joined with hypens.
11115-1-W
There may be a reason for it, but most likely this violates first normal form and will make the database hard to query. It's part of the reason why such a complicated query is needed. If the SKU column really represents multiple things then we may want to consider breaking it out into MasterSKU, Version, and Color or whatever each hyphen represents.

How to make a single MySQL query that uses the results of another query

I have a Perl program that queries a MySQL database to bring back results based upon which "report" option a user has selected from a web page.
One of the reports is all occupants of a student housing building who have applied for a parking permit, but who have not yet been given one.
When the students apply for a permit, it records the specifics about their car (make, model, year, color, etc.) in a single table row. Each apartment can have up to three students, and each student may apply for a permit. So an apartment might have 0 permits, or 1, 2, or 3 permits, depending upon how many of them have cars.
What I'd like to be able to do, is execute a MySQL query that will find out how many occupants in each apartment have applied for a parking permit, and then based on the results of that query, find out how many permits have been issued. If the number of permits issued is less than the number of applications, that apartment number should be returned in the result set. It doesn't have to name the specific occupant, just the fact that the apartment has at least one occupant who has applied for a permit, but not yet received one.
So I have two tables, one is called occupant_info and it contains all kinds of info about the occupant, but the relevant fields are:
counter (a unique row id)
parking_permit_1_number
parking_permit_2_number
parking_permit_3_number
When a parking permit has been assigned, it is recorded in the appropriate parking_permit_#_number field (if it's occupant number one's permit, it would be recorded in parking_permit_1_number, etc.).
The second table is called, parking_permits, and contains all of the car/owner specifics (make, model, year, owner, owner address, etc.). It also contains a field which references the counter from the occupant_info table.
So an example would be:
occupant_info table
counter | parking_permit_1_number | parking_permit_2_number | parking_permit_3_number
--------|-------------------------|-------------------------|------------------------
1 | 12345 | | 98765
2 | 43920 | |
3 | 30239 | | 34233
parking_permits table
counter | counter_from_occupant_info | permit_1_name | permit_2_name | permit_3_name
--------|----------------------------|---------------|-----------------|-------------------
1 |2 | David Jones | James Cameron | Michael Smerconish
2 |3 | Bill Epps | Hillary Clinton | Donald Trump
3 |1 | Joanne Miller | | Sridevi Gupta
I want a query that will first look at how many occupants in an apartment have applied for a permit. This is determined by counting the names in the parking_permits table. In that table, row 1 has three names, row 2 has three names, and row 3 has two names. The query should then look at the occupant_info table, and for each counter_from_occupant_info from the parking_permits table, see if the same number of parking permits have been issued. This can be determined by comparing the number of non-blank parking_permit_#_number fields.
Using the data above, the query would see the following :
parking_permit table row 1
Has counter_from_occupant_info equal to "2"
Has three names
The row in occupant_info with counter = "2" has only one permit number issued,
so counter_from_occupant_info 2 from parking_permits should be in the result set.
parking_permit table row 2
Has counter_from_occupant_info equal to "3"
Has three names
The row in occupant_info with counter = "3" has only two permit numbers issued,
so counter_from_occupant_info 3 from parking_permits should be in the result set.
parking_permit table row 3
Has counter_from_occupant_info equal to "1"
Has two names
The row in occupant_info with counter = "1" has two permit numbers issued,
so this row should *not* be in the result set.
I've thought about using if, then, case, when, type logic to do this in one query, but frankly can't wrap my head around how to do so.
I was thinking something like:
SELECT
CASE WHEN ( SELECT counter_from_occupant_info
FROM parking_permits
WHERE parking_permit_1_name != ""
AND parking_permit_2_name != ""
AND parking_permit_3_name != "" ) THEN
IF ( SELECT parking_permit_1_number,
parking_permit_2_number,
parking_permit_3_number
FROM occupant_info
WHERE counter = ***somehow reference counter from above case statement--I don't know how to do this***
But then my head explodes and I realize I don't know what the heck I'm doing.
Any help would be appreciated. :-)
Doug
You have a few problems:
Your occupants table schema is bad. There's worse out there, but it looks like someone that doesn't understand how a database works built this table.
Your permits table is also bad. Same reason.
You have no idea what you are doing (kidding... kidding...)
Problem 1:
Your occupants table should probably be two tables. Because an occupant could have 0-3 permits (possibly more, I can't tell from the sample data) then you need a table for your occupant's attributes (name, height, gender, age, primary smell, favorite color, first rent date, I dunno).
Occupants
OccupantID | favorite TV Show | number of limbs | first name | last name | aptBuilding
And... another table for Relationship between the occupant and the permit:
Occupant_permits
OccupantID | Permit ID | status
Now... an occupant can have as many permits as you can stuff into that table and the relationship between them has a status "Applied for", or "Granted" or "Revoked" or what have you.
Problem 2
Your permit info table is doing double duty as well. It holds the information about a permit (it's name) as well as the relationship to the occupant. Since we already have a relationship to the occupant with the "Occupant_Permits" table above, we just need a permits table to hold attributes of a permit:
Permits
Permit ID | Permit Name | Description | etc..
Problem 3
Now that you have a correct schema where objects are in their own table (Occupant, Permit, Occupant and Permit Relationship) your query to get a list of apartments that have at least one occupant that has applied, but not yet received a permit would be:
SELECT
COUNT(DISTINCT o.AptBuilding)
FROM
occupants as o
INNER JOIN occupants_permit as op
ON o.occupant_id = op.occupant_id
INNER JOIN permits as p
ON op.permit_id = p.permit_id
WHERE
op.Status = "Applied"
That's nice and simple and you aren't relying on CASE or UNION or count comparison or any fancy stuff. Just nice straight joins and a simple WHERE clause. This will be fast to query and there's no funny business.
Because your schema isn't great, in order to get something similar you'll need to make use of either UNION queries to stack your many permit_N_ fields into a single field and run something similar to the above query, or you'll have use a fair amount of CASE/IF statements:
SELECT DISTINCT p.pCounter
FROM
(
SELECT
counter as Ocounter
CASE WHEN parking_permit_1_number IS NOT NULL THEN 1 ELSE 0 END
+
CASE WHEN parking_permit_2_number IS NOT NULL THEN 1 ELSE 0 END
+
CASE WHEN parking_permit_3_number IS NOT NULL THEN 1 ELSE 0 END AS permitCount
FROM occupant_info
) as o
LEFT OUTER JOIN
(
SELECT
counter_from_occupant_info as pCounter
CASE WHEN parking_permit_1_name IS NOT NULL THEN 1 ELSE 0 END
+
CASE WHEN parking_permit_2_name IS NOT NULL THEN 1 ELSE 0 END
+
CASE WHEN parking_permit_3_Name IS NOT NULL THEN 1 ELSE 0 END AS permitPermitCount
) as p ON o.Ocounter = p.Pcounter
WHERE p.permitCounter > o.PermitCount
I'm not 100% convinced that is exactly what you are looking for since your schema is confusing where you have multiple objects in a single table and everything is pivoted, but... it should get you in the ball park.
This will be much slower too. There's intermediate result sets, CASE statements, and math, so don't expect MySQL to spit this out in milliseconds.

MySQL Query which require multiple joins

I have a system that is used to log kids' their behavior. If a child is naughty it is logged as negative and if it has a well behaviour it is logged as positive.
For instance - if a child is rude it gets a 'Rude' negative and this is logged in the system with minus x points.
My structure can be seen in this sqlfiddle - http://sqlfiddle.com/#!9/46904
In the users_rewards_logged table, the reward_id column is a foreign key linked to either the deductions OR achievements table depending on the type of column.
If type is 1 is a deduction reward, if the type value is 2 is a achievement reward.
I basically want a query to list out something like this:
+------------------------------+
| reward | points | count |
+------------------------------+
| Good Work | 100 | 1 |
| Rude | -50 | 2 |
+------------------------------+
So it tallys up the figures and matches the reward depending on type (1 is a deduction, 2 is a achievement)
What is a good way to do this, based on the sqlfiddle?
Here's a query that gets the above desired results:
SELECT COALESCE(ua.name, ud.name) AS reward,
SUM(url.points) AS points, COUNT(url.logged_id) AS count
FROM users_rewards_logged url
LEFT JOIN users_deductions ud
ON ud.deduction_id = url.reward_id
AND url.type = 1
LEFT JOIN users_achievements ua
ON ua.achievement_id = url.reward_id
AND url.type = 2
GROUP BY url.reward_id, url.type
Your SQLFiddle had the order of points and type in the wrong order for the table users_rewards_logged.
Here's the fixed SQLFiddle with the result:
reward points count
Good Work 100 1
Rude -50 2
Although eggyal is correct--this is rather bad design for your data--what you ask can be done, but requires a UNION clause:
SELECT users_achievements.name, users_rewards_logged.points, COUNT(*)
FROM users_rewards_logged
INNER JOIN users_achievements ON users_achievements.achievement_id = users_rewards_logged.reward_id
WHERE users_rewards_logged.type = 2
UNION
SELECT users_deductions.name, users_rewards_logged.points, COUNT(*)
FROM users_rewards_logged
INNER JOIN users_deductions ON users_deductions.deduction_id = users_rewards_logged.reward_id
WHERE users_rewards_logged.type = 1
GROUP BY 1, 2
There's no reason NOT to combine the achievements and deductions tables and just use non-conflicting codes. If you combined the tables, then you would no longer need the UNION clause--your query would be MUCH simpler.
I noticed that you have two tables (users_deductions and users_achievements) that defines the type of reward. As #eggyal stated, you are violating the principle of orthogonal design, which causes the lack of normalization of your schema.
So, I have combined the tables users_deductions and users_achievements in one table called reward_type.
The result is in this fiddle: http://sqlfiddle.com/#!9/813d5/6

Mixing HAVING with CASE OR Analytic functions in MySQL (PartitionQualify(?

I have a SELECT query that returns some fields like this:
Date | Campaign_Name | Type | Count_People
Oct | Cats | 1 | 500
Oct | Cats | 2 | 50
Oct | Dogs | 1 | 80
Oct | Dogs | 2 | 50
The query uses aggregation and I only want to include results where when Type = 1 then ensure that the corresponding Count_People is greater than 99.
Using the example table, I'd like to have two rows returned: Cats. Where Dogs is type 1 it's excluded because it's below 100, in this case where Dogs = 2 should be excluded also.
Put another way, if type = 1 is less than 100 then remove all records of the corresponding campaign name.
I started out trying this:
HAVING CASE WHEN type = 1 THEN COUNT(DISTINCT Count_People) > 99 END
I used Teradata earlier int he year and remember working on a query that used an analytic function "Qualify PartitionBy". I suspect something along those lines is what I need? I need to base the exclusion on aggregation before the query is run?
How would I do this in MySQL? Am I making sense?
Now that I understand the question, I think your best bet will be a subquery to determine which date/campaign combinations of a type=1 have a count_people greater than 99.
SELECT
<table>.date,
<table>.campaign_name,
<table>.type,
count(distinct count_people) as count_people
FROM
(
SELECT
date,
campaign_name
FROM
<table>
WHERE type=1
HAVING count(distinct count_people) > 99
GROUP BY 1,2
) type1
LEFT OUTER JOIN <table> ON
type1.campaign_name = <table>.campaign_name AND
type1.date = <table>.date
WHERE <table>.type IN (1,2)
GROUP BY 1,2,3
The subquery here only returns campaign/date combinations when both the type=1 AND it has greater than 99 count_people. It uses a LEFT JOIN back to the to insure that only those campaign/date combinations make it into the result set.
The WHERE on the main query keeps the results to only Types 1 and 2, which you stated was already a filter in place (though not mentioned in the question, it was stated in a comment to a previous answer).
Based on your comments to answer by #JNevill I think you will have no option but to use subselects to pre-filter the record set you are dealing with, as working with HAVING is going to limit you only to the current record being evaluated - there is no way to compare against previous or subsequent records in the set in this manner.
So have a look at something like this:
SELECT
full_data.date AS date,
full_data.campaign_name AS campaign_name,
full_data.type AS type,
COUNT(full_data.people) AS people_count
FROM
(
SELECT
date,
campaign_name,
type,
COUNT(people) AS people_count
FROM table
WHERE type IN (1,2)
GROUP BY date, campaign_name, type
) AS full_data
LEFT JOIN
(
SELECT
date,
campaign_name,
COUNT(people) AS people_count
FROM table
WHERE type = 1
GROUP BY date, campaign_name
HAVING people_count < 100
) AS filter
ON
full_data.date = filter.date
AND full_data.campaign_name = filter.campaign_name
WHERE
filter.date IS NULL
AND filter.campaign_name IS NULL
The first subselect is basically your current query without any attempt at using HAVING to filter out results. The second subselect is used to find all date/campaign name combos which have people_count > 100 and use those as a filter for against the full data set.

Hierarchical configuration select

Edit: Sorry for the confusion. Just got the OK from my boss to post this portion of the schema. I would have had more detail in the original post if I were allowed to post an image.
I have a configuration schema that looks like this:
http://img717.imageshack.us/img717/7297/heirarchy.png
Each of the levels is contained within the level below it (i.e. - a partner has multiple programs), and each config level shares config keys with the other types of config levels (i.e. - A default timezone can be set at the partner level, and then be overridden from the program, portfolio or device level).
What this allows us to do is have a default for a type of object, and then override that with more specific taxonomies. For instance:
Say I have a partner object that is a company. Say that hierarchy_configuration_key 1 is the default timezone. I put a partner_configuration that says that most often, that partner will be located on the east coast (NYC time).
Now I have multiple programs that that partner supports. Say that specific program is based out of California. I put a program_configuration that says that that devices in that program are Sacramento time.
Now let's skip portfolio, and say that someone signed up for this program based out of California moves to Denver but is still a customer. We set a Device configuration that says they're in Mountain time now.
The hierarchy looks like this:
Level |Timezone (hierarchy_configuration_key 1)
---------------------------------------------------
Partner |NYC
Program |Sacramento
Portfolio |null (defaults to most granular above it, so Sacramento)
Device |Denver
Now I want to select my configurations grouped by hierarchy_configuration_key_id:
I can use inner joins to traverse the levels, but I want a select to give me a result like this (grouped by hierarchy_configuration_key_id) for the primary key of the device (device_id):
device_id |portfolio_id |program_id |partner_id |device_config |portfolio_config |program_config| partner_config
---------------------------------------------------------------------------------------------------------------------
1 |2 |1 |35 |Denver |null |Sacramento | NYC
Also acceptable would be a Select that just gave me the most relevant config value, i.e.:
device_id |portfolio_id |program_id |partner_id |config_value
-------------------------------------------------------------
1 |2 |1 |35 |Denver
Thanks in advance. Let me know if you need any more clarification.
I think the only part that doesn't work here is pointed out by #EugenRieck's comment...
- Which field tells the Miata it is a Child of Mazda?
I would change the structure slightly...
ENTITY_TABLE
entity_id | parent_entity_id | entity_name
1 NULL Vehicle
2 1 Car
3 2 Mazda
4 3 Miata
5 1 Cycle
6 5 Unicycle
7 6 Broken Unicycle
PROPERTY_TABLE
entity_id | property_type | value
1 Wheels 4
2 Wheels NULL
3 Wheels NULL
4 Wheels NULL
5 Wheels 2
6 Wheels 1
7 Wheels 0
(And repeated for other property types as appropriate)
-- Every entity must have the same properties as the parents
-- (otherwise you have to find the topmost parent first to know what properties exist)
-- An entity may only have 1 parent
-- The topmost parent must have a NULL parent_id
-- The bottommost parent must be no more than 3 joins away from the topmost parent
Then you can have something like this...
SELECT
entity1.id,
property1.property_type,
entity1.name,
entity2.name,
entity3.name,
entity4.name,
property1.value,
property2.value,
property3.value,
property4.value,
COALESCE(property1.value, property2.value, property3.value, property4.value) AS inherited_value
FROM
entity AS entity1
LEFT JOIN
entity AS entity2
ON entity2.id = entity1.parent_id
LEFT JOIN
entity AS entity3
ON entity3.id = entity2.parent_id
LEFT JOIN
entity AS entity4
ON entity4.id = entity3.parent_id
INNER JOIN
property AS property1
ON property1.entity_id = entity1.id
LEFT JOIN
property AS property2
ON property2.entity_id = entity2.id
AND property2.property_type = property1.property_type
LEFT JOIN
property AS property3
ON property3.entity_id = entity3.id
AND property3.property_type = property1.property_type
LEFT JOIN
property AS property4
ON property4.entity_id = entity4.id
AND property4.property_type = property1.property_type
WHERE
entity1.id = #entity_id
AND property1.property_type = #property_type
This solution is based on your schema with #param1 being the hierarchy_configuration_key_id and #param2 being the desired device_id. It uses a method similar to Dems' although it was arrived at independently except for my borrowing of COALESCE.
SELECT *,
IF(dv_key IS NOT NULL,'device',IF(pf_key IS NOT NULL,'portfolio',IF(pg_key IS NOT NULL,'program',IF(pt_key IS NOT NULL,'partner',NULL)))) AS hierarchy_level,
COALESCE(dv_key,pf_key,pg_key,pt_key) AS key_id,
COALESCE(dv_value,pf_value,pg_value,pt_value) AS value
FROM
(SELECT sim_id,
dv.device_id, pt.partner_id, pg.program_id, pf.portfolio_id,
dvc.hierarchy_configuration_key_id AS dv_key, dvc.configuration_value AS dv_value,
pfc.hierarchy_configuration_key_id AS pf_key, pfc.configuration_value AS pf_value,
pgc.hierarchy_configuration_key_id AS pg_key, pgc.configuration_value AS pg_value,
ptc.hierarchy_configuration_key_id AS pt_key, ptc.configuration_value AS pt_value
FROM device dv
LEFT JOIN portfolio pf USING(portfolio_id)
LEFT JOIN program pg USING(program_id)
LEFT JOIN partner pt USING(partner_id)
LEFT JOIN device_configuration dvc ON dv.device_id=dvc.device_id AND dvc.hierarchy_configuration_key_id=#param2 AND dvc.active='true'
LEFT JOIN portfolio_configuration pfc ON pf.portfolio_id=pfc.portfolio_id AND pfc.hierarchy_configuration_key_id=#param2 AND pfc.active='true'
LEFT JOIN program_configuration pgc ON pg.program_id=pgc.program_id AND pgc.hierarchy_configuration_key_id=#param2 AND pgc.active='true'
LEFT JOIN partner_configuration ptc ON pt.partner_id=ptc.partner_id AND ptc.hierarchy_configuration_key_id=#param2 AND ptc.active='true'
WHERE dv.device_id = #param1) hierchy;