I'm attempting to create additional columns for when a drive record has multiple other records assigned to the drive. In my attached screenshot, you can see there are three drives where all data is constant, but each has multiple incentives assigned to the drive.
Would a PIVOT function work to create an additional column for the incentive? I'm not looking for something dynamic and I would be OK with hard coding at the most 4 additional columns to have space for five incentives assigned to one drive.
What I would like my results to look like would be:
The last column is how I performed used For XML Path to accomplish it, but it's not exactly what I was looking for.
Here is my SQL statement:
Select
DM.DriveID [DriveID],
DM.FromDateTime [FromDateTime],
Case When DM.OwnerType = 0 Then Acct.Name Else CD.DescLong End As [OwnerName],
Acct.AccountID [AcctID],
Inc.Description [Incentive],
Stuff ((Select ', ' + isnull(EM.Description,'')
From Production.[dbo].EquipmentMaster EM
Inner Join Production.[dbo].EquipmentDetail ED On EM.EquipmentID = ED.EquipmentID And EM.EquipmentType=3
Where ED.DriveID = DM.DriveID
For XML PATH(''), TYPE).value('.', 'varchar(max)')
,1, 2, '') as [XML_Incentives]
From
Production.[dbo].rpt_DriveMaster DM
Left Outer Join Production.[dbo].rpt_Accounts Acct on DM.AccountID=Acct.AccountID
Inner Join Production.[dbo].rpt_CenterDetail CD on DM.CenterID=CD.CenterID
Left Outer Join Production.[dbo].OBI_Incentives Inc on DM.DriveID=Inc.DriveID
Where
DM.DriveID in (658946,597611,651136)
I'm looking at the link provided when marked as duplicate, but is almost like reading Greek to me. I'm trying to modify my SQL to use this as a basis, but not having much luck:
Any chance on some guidance on how this works?
Select
*
From
( Select
DM.DriveID [DriveID],
DM.FromDateTime [FromDateTime],
Case When DM.OwnerType = 0 Then Acct.Name Else CD.DescLong End As [OwnerName],
Acct.AccountID [AcctID],
Inc.Description [Incentive1],
null [Incentive2],
null [Incentive3],
null [Incentive4],
null [Incentive5]
From
Production.[dbo].rpt_DriveMaster DM
Left Outer Join Production.[dbo].rpt_Accounts Acct on DM.AccountID=Acct.AccountID
Inner Join Production.[dbo].rpt_CenterDetail CD on DM.CenterID=CD.CenterID
Left Outer Join Production.[dbo].OBI_Incentives Inc on DM.DriveID=Inc.DriveID
Where
DM.DriveID in (658946,597611,651136)
) as P
Pivot (
min(P.[Incentive])
for P.[DriveID] in ([Incentive1], [Incentive2], [Incentive3])
) as PIV
Related
We have a scenario where users answer some questions related to a parent entity that we'll call a widget. Each question has both a numeric and word answer. Multiple users answer each question for a given widget.
We then display a row for each widget with the average numeric answer for each question. We do that using a MySQL pseudo-pivot with dynamic columns as detailed here So we end up with something like:
SELECT widget_id, ...
ROUND(IFNULL(AVG(CASE
WHEN LOWER(REPLACE(RQ.question, ' ', '_')) = 'overall_size' THEN
if(RA.num = '', 0, RA.num) END),0) + .0001, 2) AS `raw_avg_overall_size`,
...
... where overall_size would be one of the question types related to the widget and might have "answers" from 5 users like 1,2,2,3,1 to that question for a given widget_id based on the answer options below:
Answers
answer_id
answer_type
num
word
111
overall_size
1
x-large
112
overall_size
2
large
113
overall_size
3
medium
114
overall_size
4
small
115
overall_size
5
x-small
So we would end up with a row that had something like this:
widget_id
average_overall_size
115
1.80
What we can't figure out is then given if we round 1.80 to zero precision we get 2 in this example which is the word value 'large' from our data above. We like to include that in the query output too so that end up with:
widget_id
raw_average_overall_size
average_overall_size
115
1.80
large
The issue is that we do not know the average for the row until the query runs. So how can we then reference the word value for that average answer in the same row when executing the query?
As mentioned we are pivoting into a variable and then run another query for the full execution. So if we join in the pivot section, that subquery looks something like this:
SET #phase_id = 1;
SET SESSION group_concat_max_len = 100000;
SET #SQL = NULL;
SET #NSQL = NULL;
SELECT GROUP_CONCAT(DISTINCT
CONCAT(
'ROUND(IFNULL(AVG(CASE
WHEN LOWER(REPLACE(RQ.short_question, '' '', ''_'')) = ''',
nsq,
''' THEN
if(RA.answer = '''', 0, RA.answer) END),0) + .0001, 2) AS `',
CONCAT('avg_raw_',nsq), '`,
REF.value, -- <- ******* THIS FAILS **** --
ROUND(IFNULL(STDDEV(CASE
WHEN LOWER(REPLACE(RQ.short_question, '' '', ''_'')) = ''',
nsq,
''' THEN RA.answer END), 0) + .0001, 3) AS `',
CONCAT('std_dev_', nsq), '`
'
)
ORDER BY display_order
) INTO #NSQL
FROM (
SELECT FD.ref_value, FD.element_name, RQ.display_order, LOWER(REPLACE(RQ.short_question, ' ', '_')) as nsq
FROM review_questions RQ
LEFT JOIN form_data FD ON FD.id = RQ.form_data_id
LEFT JOIN ref_values RV on FD.ref_value = RV.type
WHERE RQ.phase_id = #phase_id
AND FD.element_type = 'select'
AND RQ.is_active > 0
GROUP BY FD.element_name
HAVING MAX(RV.key_name) REGEXP '^[0-9]+$'
) nq
/****** suggested in 1st answer ******/
LEFT JOIN ref_values REF ON REF.`type` = nq.ref_value
AND REF.key_name = ROUND(CONCAT('avg_raw_',nsq), 0);
So we need the word answer (from the REF join's REF.value field in the above code) in the pivot output, but it fails with 'Unknown column REF.value. If we put REF.value in it's parent query field list, that also fails with the same error.
You'll need to join the table/view/query again to get the 'large' value.
For example:
select a.*, b.word
from (
-- your query here
) a
join my_table b on b.answer_id = a.answer_id
and b.num = round(a.num);
An index on my_table (answer_id, num) will speed up the extra search.
This fails, leading to the default of "2":
LOWER(REPLACE(RQ.question, ' ', '_')) = 'overall_size'
That is because the question seems to be "average_overall_size", not "overall_size".
String parsing and manipulation is the pits in SQL; suggest using the application to handle such.
Also, be aware that you may need a separate subquery to compute aggregate (eg AVG()), else it might not be computed over the set of values you think.
Query into temp table, then join
First query should produce table as follows:
CREATE temp table, temp_average_size
widget_id
average_overall_size
rounded_average_size
115
1.80
2
LEFT JOIN
select s.*, a.word
from temp_average_size s LEFT JOIN answers a
ON (s.rounded_average_size = a.num AND a.answer_type = 'overall_size)
I am having issues pulling in null values in my query. I am looking for patients who have a specific document name in their chart but also want to show patients who do not have this specific document name as well. Right now my code is only pulling in the patients with the document name History and Physical (Transcription) but I need to see Null values as well. Below is my code:
snip of code
SELECT CV3ClientVisit.ClientDisplayName, CV3ClientVisit.CurrentLocation, CV3ClientVisit.IDCode, CV3ClientVisit.VisitIDCode, CV3ClientVisit.VisitStatus, CV3ClientVisit.TypeCode, CV3ClientDocumentCUR.DocumentName
FROM CV3ClientVisit INNER JOIN
CV3ClientDocumentCUR ON CV3ClientVisit.GUID = CV3ClientDocumentCUR.ClientVisitGUID
WHERE (CV3ClientVisit.VisitStatus = 'ADM') AND (CV3ClientVisit.TypeCode = 'INPATIENT ADMIT') AND (CV3ClientDocumentCUR.DocumentName = 'History & Physical (transcription)' OR CV3ClientDocumentCUR.DocumentName IS NULL )
Use a LEFT JOIN with the condition in the ON clause:
SELECT cv.ClientDisplayName, cv.CurrentLocation, cv.IDCode,
cv.VisitIDCode, cv.VisitStatus, cv.TypeCode, cd.DocumentName
FROM CV3ClientVisit cv LEFT JOIN
CV3ClientDocumentCUR cd
ON cv.GUID = cd.ClientVisitGUID AND
cd.DocumentName = 'History & Physical (transcription)'
WHERE cv.VisitStatus = 'ADM' AND
cv.TypeCode = 'INPATIENT ADMIT' ;
I also added table aliases to simplify the query.
I have a nasty, nasty data layout that I am forced to work with. I finally got a working query using C# and a for loop executing the same query over and over but adjusting which fields are called, but now I am wondering if it is possible to do it with a while loop. I am getting an error, and I am not sure if it is because I am using Faircom / C-tree as the database, or if there is something wrong with my query. I am normally a Mysql user.
the table has 20 fields I care about and want to extract into a csv list. They are codetype1-codetype20 and I want it to be something like value1, value2, value3... where as it is now I get them all back one at a time. Trouble is that codetype1 is dependent on another field to determine where I go look for the info on that code, which is why the case statements.
DROP PROCEDURE IF EXISTS proc_loop_test;
CREATE PROCEDURE proc_loop_test()
SET #index = 1;
WHILE(#index < 21) DO
SELECT Replace(Concat(To_char(apptid), To_char(.#index) ), ' ', '') AS reference_id,
apptid AS a_reference_id,
CASE
WHEN c.ee > 0 THEN d.amt
ELSE insfee.amt
END AS amount,
CASE
WHEN c.ee > 0 THEN Rtrim(e.moneyname)
ELSE insname.namefeecatid
END AS moneyschedule_name,
CASE codetype#index
WHEN 1 THEN rtrim(a.descript)
ELSE rtrim(b.descript0)
END AS description,
CASE codetype#index
WHEN 1 THEN rtrim(a.abbrevdescript)
ELSE rtrim(b.abbrev0)
END AS abbreviated_description,
CASE codetype#index
WHEN 1 THEN rtrim(a.thiscode)
ELSE rtrim(b.thiscode0)
END AS code
FROM meetings
LEFT JOIN
(
SELECT admin.table2.procid,
admin.table2.this_code_id,
admin.v_table1.descript,
admin.v_table1.abbrevdescript,
admin.v_table1.thiscode
FROM admin.table2
INNER JOIN admin.v_table1
ON admin.table2.this_code_id = admin.v_table1.this_code_id) AS a
ON meetings.codeid#index = a.procid
LEFT JOIN
(
SELECT admin.v_table1.descript AS descript0,
admin.v_table1.abbrevdescript AS abbrev0,
admin.v_table1.thiscode AS thiscode0,
admin.v_table1.this_code_id
FROM admin.v_table1) AS b
ON meetings.codeid#index = b.this_code_id
LEFT JOIN
(
SELECT patid AS id,
ee
FROM admin.customer) AS c
ON meetings.patid = c.id
LEFT JOIN
(
SELECT this_code_id AS redid,
eecategoryid,
amt
FROM admin.eeule) AS d
ON c.ee = d.eecategoryid
AND d.redid = b.this_code_id
LEFT JOIN
(
SELECT eecategoryid AS namefeecatid,
moneyname
FROM admin.eeulenames) AS e
ON d.eecategoryid = e.namefeecatid
LEFT JOIN (SELECT pi.customer_id,
pi.primarykk_id AS picid,
pi.primarykk_name,
pi.first_name,
pi.last_name,
i.groupname,
i.ee
FROM admin.v_pir AS pi
LEFT JOIN admin.money AS i
ON pi.primarykk_id = i.insid) AS
ins
ON ins.customer_id = c.id
LEFT JOIN (SELECT this_code_id AS redid,
eecategoryid,
amt
FROM admin.eeule) AS insfee
ON ins.ee = insfee.eecategoryid
AND insfee.redid = b.this_code_id
LEFT JOIN (SELECT eecategoryid AS namefeecatid,
moneyname
FROM admin.eeulenames) AS insname
ON insfee.eecategoryid = insname.namefeecatid
WHERE codeid#index >= 1
END WHILE;
END;
I have never used a while loop, and while I understand somewhat I am supposed to select this to go INTO something, do I need to create a temp table, or can it all just be stored in memory till the end of the loop and returned.
For what it is worth, the entire SELECT query works in C# when you replace the #index with concatenating format " . index . "
Based on the information that you have provided, it is strongly suggested that you reach out to your vendor for this. You're attempting to create a stored procedure, however, using a mySQL proprietary syntax. Stored procedure support is unique to each database vendor. FairCom's c-treeACE SQL actually uses Java for cross platform support and .NET for Windows.
https://docs.faircom.com/doc/jspt/#cover.htm
Stored procedure development requires a strong knowledge of the database layout which is highly application dependent. In this case, many legacy application dependencies may be involved. Again, your best source of information will be your application vendor.
If anyone can help me rewrite my query to work in mysql 5 I would be very grateful. If anyone can provide links to solid, simple tutorials on how to rewrite old queries that would also be great.
My current (version 4) query looks like this:
SELECT
course.course_code,
course.course_title_sv AS course_title,
course.course_u_credits,
course.course_successive_level_scb_id,
s.successive_level_scb_order,
s.successive_level_scb_code,
LEFT (education_level.edu_level_name_sv, 1) AS course_edu_level, course.course_level,
GROUP_CONCAT(DISTINCT h.head_area_hv_title_sv SEPARATOR ', ') AS head_area_hv
FROM
course, course_event, course_event_package_links, package, education_level
LEFT JOIN
course_has_head_area_hv ON(course.course_id = course_has_head_area_hv.course_id)
LEFT JOIN
head_area_hv h ON(h.head_area_hv_id = course_has_head_area_hv.head_area_hv_id)
LEFT JOIN
successive_level_scb s ON(s.successive_level_scb_id = course.course_successive_level_scb_id)
WHERE
course.course_edu_level=education_level.edu_level_id AND
course.course_id=course_event.course_id AND
course_event.course_event_id=course_event_package_links.course_event_id AND
course_event_package_links.package_id=package.package_id AND
course.course_successive_level_scb_id != '' AND
package.package_id='6318'
GROUP BY course.course_id
Simply replace
SELECT ...
FROM course, course_event, course_event_package_links
...
WHERE course.course_id=course_event.course_id
AND course_event.course_event_id=course_event_package_links.course_event_id
by
SELECT ...
FROM course
JOIN course_event ON course_event.course_id = course.course_id
JOIN course_event_package_links
ON course_event_package_links.course_event_id = course_event.course_event_id
...
About your error message, are you sure course.course_id exists? Maybe it was replaced by course.course_code?
I have relationships that might not necessarily exist (they could be optional i.e. null); for example, a image may not have an address so it may be null.
I am unsure how to not return all null values.
Is there some condition I can put in place on the join that says if the address is null don't do a join and don't return all the null columns?
SELECT im.title, im.alias_title, im.description, im.main_image, im.hits,
im.show_comment, im.created_on, im.date_taken, im.account_type_id,
c.make, c.model, ad.address_line_1, ad.address_line_2,
spc.state_province_county, tvc.town_village_city, co.country,
ge.latitude, ge.longitude, ge.zoom, ge.yaw, ge.pitch,
us.first_name, us.surname, us.user_set_online, ut.username,
ut.account_type_id, aty.`type`, ufy.realname, ufy.location,
ufy.location, ufy.account_type_id
FROM image im
INNER JOIN user us
ON im.user_id = us.id
LEFT JOIN user_type ut
ON us.id = ut.user_id
LEFT JOIN user_flickr_youtube ufy
ON ut.id = ufy.user_type_id
LEFT JOIN account_type aty
ON ut.account_type_id =aty.id
LEFT JOIN address ad
ON im.address_id = ad.id
LEFT JOIN state_province_county spc
ON ad.state_province_county_id = spc.id
LEFT JOIN town_village_city tvc
ON ad.town_village_city_id =tvc.id
LEFT JOIN country co
ON ad.country_id =co.id
LEFT JOIN geolocation ge
ON im.geolocation_id = ge.id
LEFT JOIN camera c
ON im.camera_id = c.id
WHERE im.alias_title = 'test'
AND im.approved = 'Yes'
AND im.visible = '1'
LIMIT 1;
Is there some condition i can put in place on the join that says if the address is null dont do a join and dont bring me back all the null columns
Yes; you can run a JOIN instead of a LEFT JOIN. But that won't simply exclude the address if it is NULL, it will ignore the whole row altogether.
Usually this kind of situation is either handled by supplying a default value, possibly empty, for example directly in MySQL
SELECT
...COALESCE(ad.address_line_1,'(no address)') AS address_line_1,
COALESCE(ad.address_line_2,'') AS address_line_2, ...
or it is handled by the application:
if row['address_line_1']:
result = result + ("<td class=\"address\">%s</td>" % ( row['address_line_1'] ))
...
This also because a query could potentially return not one record, but several, and of these, some might have a NULL colum and some might not.
UPDATE
There is a way, but it's likely to make milk go sour in cows fifty miles downrange.
This is a proof of concept, on a MUCH smaller query and table, and takes advantage of the possibility of dynamically building a query.
First of all we have our query WHERE condition, here represented by "id = 1". We want to have the name column if the name column is not NULL.
SELECT #address := COALESCE(MIN(',name'),'') FROM client WHERE name IS NOT NULL AND id = 1;
This will return an empty string if the selected column is NULL. Otherwise it will return a comma and the name of that column.
This is the statement that in your case will be humongous, given your query. It contains the same WHERE as before, without the request that the name be NULL. And the field list is now dynamic.
SELECT #string := CONCAT('SELECT id', #address, ' FROM client WHERE id = 1');
Except that #string is, well, a string. To execute it as a query we do
PREPARE query FROM #string;
EXECUTE query;
DEALLOCATE PREPARE query;
How this might interact with your application, I do not dare fathom. I have tried an implementation in PHP on an expendable VM :-), cycling between the values of 1 and 3 (one row has a NULL name, one hasn't).
<?php
// Connect to this VM's local DB
mysql_connect('localhost','root','') or die("Cannot connect");
mysql_select_db('test');
foreach(array(1, 3) as $id)
{
mysql_query("SELECT #address := COALESCE(MIN(',name'),'') FROM client WHERE name IS NOT NULL AND id = $id;");
mysql_query("SELECT #string := CONCAT('SELECT id', #address, ' FROM client WHERE id = ', $id);");
mysql_query("PREPARE query FROM #string;");
$exec = mysql_query("EXECUTE query;");
while($tuple = mysql_fetch_assoc($exec))
{
print implode(" | ", $tuple) . "\n";
}
mysql_query("DEALLOCATE PREPARE query;");
}
?>
The answer seems to indicate it's working:
1 | Rossi
3
(I wouldn't have been surprised if it returned something like 'Ia! Cthulhu fhtagn!').