I hope you are well.
I need your help and advice on a repetitive task that I would like to automate.
Every month my manager asks me to run a series of SQL queries based on the same table and columns.
I thought I could create a rules table that would allow him to create his own queries
So I created this rules table like this:
Based on this table I would like to successively execute 3 queries.
The first query is for the rule '+NET 3 or More' and the query is like that:
SELECT * FROM Table Where
([QuestionCode] = 'HSZ' AND ResponseCode = 3)
OR ([QuestionCode] = 'HSZ' AND ResponseCode = 4)
The Second Query is for the Rule 'Age Between 25-35' and the query is like that:
SELECT * FROM Table Where
([QuestionCode] = 'RS2' AND ResponseCode >= 25)
AND ([QuestionCode] = 'RS2' AND ResponseCode < 35)
The 3rd query is for the rule 'CHN' and 'HSZ' and the query is like that:
SELECT * FROM Table Where
([QuestionCode] = 'CHN' AND ResponseCode = 5)
AND ([QuestionCode] = 'HSZ' AND ResponseCode = 1)
I would like your opinion on this solution and especially have your help to create this dynamic query
Here is the script to create the rule table:
WITH CTE AS
(
SELECT RuleId = 1
, NetQuestionCode = '+NET 3 or More'
, QuestionCategory = 'HSZ'
, QuestionCode = 'HSZ'
, ResponseOperator = '='
, ResponseCode = '3'
, RuleOrder = '1'
, CategoryRule = 'OR'
UNION ALL
SELECT RuleId = 1
, NetQuestionCode = '+NET 3 or More'
, QuestionCategory = 'HSZ'
, QuestionCode = 'HSZ'
,ResponseOperator = '='
, ResponseCode = '4'
, RuleOrder = '2'
, CategoryRule = 'OR'
UNION ALL
SELECT RuleId = 2
, NetQuestionCode = 'Age Between 25-35'
, QuestionCategory = 'RS2'
, QuestionCode = 'RS2'
,ResponseOperator = '>='
, ResponseCode = '25'
, RuleOrder = '1'
, CategoryRule = 'and'
UNION ALL
SELECT RuleId = 2
, NetQuestionCode = 'Age Between 25-35'
, QuestionCategory = 'RS2'
, QuestionCode = 'RS2'
,ResponseOperator = '<'
, ResponseCode = '35'
, RuleOrder = '2'
, CategoryRule = 'and'
UNION ALL
SELECT RuleId = 3
, NetQuestionCode = 'CHN AND HSZ'
, QuestionCategory = 'CHN'
, QuestionCode = 'CHN'
,ResponseOperator = '='
, ResponseCode = '5'
, RuleOrder = '1'
, CategoryRule = 'and'
UNION ALL
SELECT RuleId = 3
, NetQuestionCode = 'CHN AND HSZ'
, QuestionCategory = 'CHN'
, QuestionCode = 'HSZ'
,ResponseOperator = '='
, ResponseCode = '1'
, RuleOrder = '2'
, CategoryRule = 'and'
)
SELECT *
Into [dbo].[Rules_Parameters]
FROM CTE
Thank you for your help.
Try this out, I based it on your CTE, I added more common table expressions, to build the dynamic part of the WHERE, then used another one to group the conditions for the same rule based on the operator and finally move it to a variable that will be used with the dynamic sql.
declare #DynamicQuery nvarchar(max)=''
; WITH CTE AS
(
SELECT RuleId = 1
, NetQuestionCode = '+NET 3 or More'
, QuestionCategory = 'HSZ'
, QuestionCode = 'HSZ'
, ResponseOperator = '='
, ResponseCode = '3'
, RuleOrder = '1'
, CategoryRule = 'OR'
UNION ALL
SELECT RuleId = 1
, NetQuestionCode = '+NET 3 or More'
, QuestionCategory = 'HSZ'
, QuestionCode = 'HSZ'
,ResponseOperator = '='
, ResponseCode = '4'
, RuleOrder = '2'
, CategoryRule = 'OR'
UNION ALL
SELECT RuleId = 2
, NetQuestionCode = 'Age Between 25-35'
, QuestionCategory = 'RS2'
, QuestionCode = 'RS2'
,ResponseOperator = '>='
, ResponseCode = '25'
, RuleOrder = '1'
, CategoryRule = 'and'
UNION ALL
SELECT RuleId = 2
, NetQuestionCode = 'Age Between 25-35'
, QuestionCategory = 'RS2'
, QuestionCode = 'RS2'
,ResponseOperator = '<'
, ResponseCode = '35'
, RuleOrder = '2'
, CategoryRule = 'and'
UNION ALL
SELECT RuleId = 3
, NetQuestionCode = 'CHN AND HSZ'
, QuestionCategory = 'CHN'
, QuestionCode = 'CHN'
,ResponseOperator = '='
, ResponseCode = '5'
, RuleOrder = '1'
, CategoryRule = 'and'
UNION ALL
SELECT RuleId = 3
, NetQuestionCode = 'CHN AND HSZ'
, QuestionCategory = 'CHN'
, QuestionCode = 'HSZ'
,ResponseOperator = '='
, ResponseCode = '1'
, RuleOrder = '2'
, CategoryRule = 'and'
),WhereCondition as (
SELECT *,'([QuestionCode] = '''+QuestionCode+''' AND ResponseCode '+ResponseOperator+' '+ResponseCode+')' [Condition] FROM CTE
),G as (
select *,cast(Condition as varchar(max)) WhereAll from WhereCondition where RuleOrder=1
union all
select c.*,WhereAll+' '+c.CategoryRule+' '+c.Condition from G
inner join WhereCondition c on c.RuleOrder=(G.RuleOrder+1) and c.RuleId=G.RuleId
),qq as (
select top(3) 'SELECT * FROM Table Where '+WhereAll+';' [q] from G order by RuleOrder desc
)
select #DynamicQuery=#DynamicQuery+q from qq
EXECUTE sp_executesql #DynamicQuery
or if you want it from the [Rules_Parameters], the change is as below:-
;with WhereCondition as (
SELECT *,'([QuestionCode] = '''+QuestionCode+''' AND ResponseCode '+ResponseOperator+' '+ResponseCode+')' [Condition] FROM [dbo].[Rules_Parameters]
),G as (
select *,cast(Condition as varchar(max)) WhereAll from WhereCondition where RuleOrder=1
union all
select c.*,WhereAll+' '+c.CategoryRule+' '+c.Condition from G
inner join WhereCondition c on c.RuleOrder=(G.RuleOrder+1) and c.RuleId=G.RuleId
),qq as (
select top(3) 'SELECT * FROM Table Where '+WhereAll+';' [q] from G order by RuleOrder desc
)
select #DynamicQuery=#DynamicQuery+q from qq
EXECUTE sp_executesql #DynamicQuery
In either way the #DynamicQuery variable will contain the below:-
SELECT * FROM Table Where ([QuestionCode] = 'CHN' AND ResponseCode = 5) and ([QuestionCode] = 'HSZ' AND ResponseCode = 1);SELECT * FROM Table Where ([QuestionCode] = 'RS2' AND ResponseCode >= 25) and ([QuestionCode] = 'RS2' AND ResponseCode < 35);SELECT * FROM Table Where ([QuestionCode] = 'HSZ' AND ResponseCode = 3) OR ([QuestionCode] = 'HSZ' AND ResponseCode = 4);
Hope this is what you want.
Note: I did not put any validations, for example ResponseOperator may have an operator that will not work and break the query , etc.
Note: I did not use STRING_AGG as you did not mention your sql database version.
Related
I have a DB2 query as below. I am looking for ways to improve the speed of this one. I have tried visual explain but no indexes were advised by the index advisor. Can somebody have a look at this and advise if something can be done?
There is
CREATE OR REPLACE VIEW PSCMPORDVW
AS
WITH INPROGRESS AS
(
SELECT
DIODR#
, DIDISP
, DIUNIT
, DISTST
, DIAPRV
, DIETAD
, DITRLR AS TRAILER_ID
, DIDR1
, DIETAT
FROM
LOAD
WHERE
DIETAD <> 0
AND DIETAT <> '0000'
ORDER BY
1
)
, STOPGROUP AS
(
SELECT
SOORD STOPORDER
, COUNT(*) STOPSREMAIN
, MIN(SOSTP#) NEXTSTOP
, MAX(SOAPPR) APPTREQ
FROM
STOPOFF
INNER JOIN
INPROGRESS
ON
DIODR# = SOORD
WHERE
SOARDT = 0
GROUP BY
SOORD
ORDER BY
1
)
, STOPAPPTS AS
(
SELECT
SOORD APPTORDER
, SOCUST STOPCUST
, SOEDA ETADATE
, SOETA ETATIME
, SOADT1 EARLYDATE
, SOATM1 EARLYTIME
, SOADT2 LATEDATE
, SOATM2 LATETIME
, SOCTYC NEXTCITY
, SOSTP# APPTSTOP
, SOST NEXTSTATE
FROM
STOPOFF
INNER JOIN
STOPGROUP
ON
STOPORDER = SOORD
AND NEXTSTOP = SOSTP#
)
SELECT
ORDER_NUMBER
, SHIPPER_ID
, SHIPPER_NAME
, SHIPPER_ADDRESS_1
, SHIPPER_ADDRESS_2
, SHIPPER_CITY
, SHIPPER_ST
, SHIPPER_ZIP
, SHIPPER_ZIP_EXT
, LOAD_AT_ID
, LOAD_AT_NAME
, LOAD_AT_ADDRESS_1
, LOAD_AT_ADDRESS_2
, LOAD_AT_CITY
, LOAD_AT_ST
, LOAD_AT_ZIP
, LOAD_AT_ZIP_EXT
, LOAD_AT_LATITUDE
, LOAD_AT_LONGITUDE
, EARLY_PU_DATE_TIME
, LATE_PU_DATE_TIME
, EARLY_DELV_DATE_TIME
, EST_REVENUE
, ORDER_DIV
, CONSIGNEE_ID
, CONSIGNEE_NAME
, CONSIGNEE_ADDRESS_1
, CONSIGNEE_ADDRESS_2
, CONSIGNEE_CITY
, CONSIGNEE_ST
, CONSIGNEE_ZIP
, CONSIGNEE_ZIP_EXT
, CONSIGNEE_LATITUDE
, CONSIGNEE_LONGITUDE
, TRAILER_TYPE
, ORDER_MESSAGE
, ADDITIONAL_STOPS
, CMDTY_CODE
, CMDTY_DESCRIPTION
, ORDER_MILES
, ORDER_WGT
, ORIGIN_CITY_CODE
, ORIGIN_CITY
, ORIGIN_ST
, DEST_CITY_CODE
, DEST_CITY_NAME
, DEST_ST
, PICK_UP_AREA
, PLAN_INFO
, NUMBER_LDS
, NUMBER_DISP
, SHIP_DATE_TIME
, NEW_PICKUP_AREA
, EQUIPMENT_NUMBER
, APPT_REQ
, APPT_MADE
, PRE_T_SEQ
, PRE_T_AREA
, LOAD_DISPATCHED
, CUST_SERV_REP
, NEGOTIATIONS
,
(
CASE
WHEN UNUNIT IS NOT NULL
THEN UNUNIT
ELSE ' '
END
)
UNIT_DISPATCHED
,
(
CASE
WHEN UNSUPR IS NOT NULL
THEN UNSUPR
ELSE ' '
END
)
DRIVER_MGR_CODE
, COALESCE(SUPNAM, ' ') DRIVER_MGR_NAME
,
(
CASE
WHEN UNFMGR IS NOT NULL
THEN UNFMGR
ELSE ' '
END
)
FLEET_MGR_CODE
, COALESCE(FLTNAM, ' ') FLEET_MGR_NAME
,
(
CASE
WHEN UNTRL1 IS NOT NULL
THEN UNTRL1
ELSE ' '
END
)
TRAILER_ID
, DIDISP DISPATCH_NUMBER
, (COALESCE(BCMCNEW, ' ')) FED_MC_ID
, DIUNIT DISPATCHED_UNIT
, CASE
WHEN UNETAD <> 0
AND UNETAT = ''
THEN CVTDATETIM(CHAR(UNETAD),'0000', (
SELECT
SUBSTR(DATA_AREA_VALUE, 1109, 2) AS TIMEZONE
FROM
TABLE(QSYS2.DATA_AREA_INFO('COMPAN', '*LIBL'))
)
)
WHEN UNETAD <> 0
THEN CVTDATETIM(CHAR(UNETAD),UNETAT, (
SELECT
SUBSTR(DATA_AREA_VALUE, 1109, 2) AS TIMEZONE
FROM
TABLE(QSYS2.DATA_AREA_INFO('COMPAN', '*LIBL'))
)
)
WHEN UNETAD = 0
THEN '0000-00-00T00:00:00-00:00'
END AS ETA_DATE_TIME
, NEXTSTOP
, CASE
WHEN SOARDT <> 0
AND SOARTM = ''
THEN CVTDATETIM(CHAR(SOARDT),'0000', (
SELECT
SUBSTR(DATA_AREA_VALUE, 1109, 2) AS TIMEZONE
FROM
TABLE(QSYS2.DATA_AREA_INFO('COMPAN', '*LIBL'))
)
)
WHEN SOARDT <> 0
THEN CVTDATETIM(CHAR(SOARDT),SOARTM, (
SELECT
SUBSTR(DATA_AREA_VALUE, 1109, 2) AS TIMEZONE
FROM
TABLE(QSYS2.DATA_AREA_INFO('COMPAN', '*LIBL'))
)
)
WHEN SOARDT = 0
THEN '0000-00-00T00:00:00-00:00'
END AS STOP_ARRIVAL_DATE_TIME
, CASE
WHEN SOLUDT <> 0
AND SOLUTM = ''
THEN CVTDATETIM(CHAR(SOLUDT),'0000', (
SELECT
SUBSTR(DATA_AREA_VALUE, 1109, 2) AS TIMEZONE
FROM
TABLE(QSYS2.DATA_AREA_INFO('COMPAN', '*LIBL'))
)
)
WHEN SOLUDT <> 0
THEN CVTDATETIM(CHAR(SOLUDT),SOLUTM, (
SELECT
SUBSTR(DATA_AREA_VALUE, 1109, 2) AS TIMEZONE
FROM
TABLE(QSYS2.DATA_AREA_INFO('COMPAN', '*LIBL'))
)
)
WHEN SOLUDT = 0
THEN '0000-00-00T00:00:00-00:00'
END AS STOP_DEPART_DATE_TIME
, ORBAMT ORDER_INV_AMT
, ORARST AR_STATUS_FLAG
, DISTST SETTLEMENT_FLAG
, DIAPRV APPROVED_FOR_PAY
, BCCARR CARRIER_CODE
, BCNAME CARRIER_NAME
, BCADDR CARRIER_ADDRESS_1
, BCADR2 CARRIER_ADDRESS_2
, BCCITY CARRIER_CITY
, BCST CARRIER_ST
, BCZIP CARRIER_ZIP
FROM
INPROGRESS
INNER JOIN
PSMAINORVW A
ON
DIODR# = ORDER_NUMBER
AND DIDISP = NUMBER_DISP
AND
(
SUBSTR(ORDER_NUMBER, 1, 2) <> 'DH'
AND SUBSTR(ORDER_NUMBER, 1, 1) <> 'M'
)
LEFT OUTER JOIN
STOPOFF
ON
DIODR# = SOORD
AND SOSTP# = 90
LEFT OUTER JOIN
LMCARR
ON
DIUNIT = BCCARR
LEFT OUTER JOIN
MMILES
ON
MMORD# = DIODR#
AND MMRECTYPE = 'D'
AND MMDSP# = DIDISP
EXCEPTION JOIN
ORDBILL B
ON
B.ORODR# = DIODR#
AND B.ORSEQ = ' '
AND ORARST = '1'
LEFT OUTER JOIN
STOPGROUP
ON
STOPORDER = DIODR#
LEFT OUTER JOIN
STOPAPPTS
ON
APPTORDER = STOPORDER
AND APPTSTOP = NEXTSTOP
LEFT OUTER JOIN
UNITS
ON
UNUNIT = DIUNIT
AND UNORD# = ORDER_NUMBER
LEFT OUTER JOIN
SUPMAST
ON
SUPCDE = UNSUPR
LEFT OUTER JOIN
FLTMAST
ON
UNFMGR = FLTCDE
WHERE
DIETAD <> 0
AND DIETAT <> '0000'
RCDFMT PSCMPORDVW ;
I suspect that the below part might be slowing it up. Can someone advise what can be done here?
STOPGROUP AS
(
SELECT
SOORD STOPORDER
, COUNT(*) STOPSREMAIN
, MIN(SOSTP#) NEXTSTOP
, MAX(SOAPPR) APPTREQ
FROM
STOPOFF
INNER JOIN
INPROGRESS
ON
DIODR# = SOORD
WHERE
SOARDT = 0
GROUP BY
SOORD
ORDER BY
1
)
Even though Visual Explain (VE) doesn't advise any indexes ... it can still be used to see how long various parts of your query are taking.
If the
SELECT
SOORD STOPORDER
, COUNT(*) STOPSREMAIN
, MIN(SOSTP#) NEXTSTOP
, MAX(SOAPPR) APPTREQ
Turns out to really be any issue, I'd look at using an Encoded Vector Index (EVI) with aggregate values to speed that up.
I'd suggest breaking it down and building back up while using VE to see where the issues lie.
The only magic wand I might suggest is putting code into a user defined table function (UDTF); assuming you currently plan to use the view like so:
select *
from myview
where something = 'somevalue';
A UDTF would allow for you to explicitly push the selection into the query
select *
from table ( myudtf('somevalue'));
I have a string to replace with the expected form.
Input: I have the following string.
'A,B,C,D,E,F,G,H,I,J,K,L'
And I want to replace the above string into the following format:
'x.A = z.A ,
x.B = z.B ,
x.C = z.C ,
x.D = z.D ,
x.E = z.E ,
x.F = z.F ,
.........
.........
x.L = z.L'
My try:
SELECT 'x.'||REPLACE('A,B,C,D,E,F,G,H,I,J,K,L',',',' = z.')
SELECT 'x.' || col || '=z.' || col
FROM (
SELECT unnest(regexp_split_to_array('A,B,C,D,E,F,G,H,I,J,K,L', ',')) col
) t
You can use string_agg and FORMAT:
SELECT string_agg(FORMAT('x.%s = z.%s', t,t) , ',')
FROM (SELECT unnest(regexp_split_to_array('A,B,C,D,E,F,G,H,I,J,K,L', ',')) AS t
) AS sub;
You can use as delimeter E',\r\n' to get carriage return:
SELECT string_agg(FORMAT('x.%s = z.%s', t,t) , E',\r\n')
FROM (SELECT unnest(regexp_split_to_array('A,B,C,D,E,F,G,H,I,J,K,L', ',')) AS t)
AS sub
Output:
x.A = z.A,
x.B = z.B,
x.C = z.C,
x.D = z.D,
x.E = z.E,
x.F = z.F,
x.G = z.G,
x.H = z.H,
x.I = z.I,
x.J = z.J,
x.K = z.K,
x.L = z.L
I have these two queries and I want one set of results combining top 5 results for TP and top 5 results for LP.
I really do not know how to explain this more clearly, I have two sets of results, top 5 for LP and top 5 for TP and I would like to have a set of results Incident_TP, IncidentID_TP, IncidentHappenedDate_TP , IncidentNumber_TP , LossValue_TP , RecoveredValue_TP , TotalLoss_TP , Incident_LP, IncidentID_LP, IncidentHappenedDate_LP , IncidentNumber_LP , LossValue_LP , RecoveredValue_LP , TotalLoss_LP
DECLARE #IncidentFromDate_TP DATE = '2011-1-12'
DECLARE #IncidentToDate_TP DATE = '2012-1-12'
DECLARE #IncidentFromDate_LP DATE = '2010-1-12'
DECLARE #IncidentToDate_LP DATE = '2011-1-12'
SELECT TOP 5
Incident_TP = Incident_TP.IncidentID
, IncidentHappenedDate_TP = Incident_TP.IncidentHappenedDate
, IncidentNumber_TP = Incident_TP.IncidentNumber
, LossValue_TP = Incident_TP.TotalLoss
, RecoveredValue_TP = Incident_TP.TotalRecovered
, TotalLoss_TP = Incident_TP.CostOfIncident
FROM
Incident AS Incident_TP
INNER JOIN Site AS Site_TP ON Incident_TP.SiteID = Site_TP.SiteID
INNER JOIN Region AS Region_TP ON Site_TP.RegionID = Region_TP.RegionID
WHERE
Incident_TP.TotalLoss > 0.00
AND Incident_TP.IncidentHappenedDate BETWEEN #IncidentFromDate_TP AND #IncidentToDate_TP
ORDER BY
TotalLoss_TP DESC
, IncidentHappenedDate_TP DESC
SELECT TOP 5
Incident_LP = Incident_LP.IncidentID
, IncidentHappenedDate_LP = Incident_LP.IncidentHappenedDate
, IncidentNumber_LP = Incident_LP.IncidentNumber
, LossValue_LP = Incident_LP.TotalLoss
, RecoveredValue_LP = Incident_LP.TotalRecovered
, TotalLoss_LP = Incident_LP.CostOfIncident
FROM
Incident AS Incident_LP
INNER JOIN Site ON Incident_LP.SiteID = Site.SiteID
INNER JOIN Region ON Site.RegionID = Region.RegionID
WHERE
Incident_LP.TotalLoss > 0.00
AND Incident_LP.IncidentHappenedDate BETWEEN #IncidentFromDate_LP AND #IncidentToDate_TP
ORDER BY
TotalLoss_LP DESC
, IncidentHappenedDate_LP DESC
Many thanks
As Tim suggests you could union the sets together, For example:
DECLARE #IncidentFromDate_TP DATE = '2011-1-12';
DECLARE #IncidentToDate_TP DATE = '2012-1-12';
DECLARE #IncidentFromDate_LP DATE = '2010-1-12';
DECLARE #IncidentToDate_LP DATE = '2011-1-12';
WITH RawData
AS ( SELECT Incident_TP = Incident_TP.IncidentID ,
IncidentHappenedDate_TP = Incident_TP.IncidentHappenedDate ,
IncidentNumber_TP = Incident_TP.IncidentNumber ,
LossValue_TP = Incident_TP.TotalLoss ,
RecoveredValue_TP = Incident_TP.TotalRecovered ,
TotalLoss_TP = Incident_TP.CostOfIncident
FROM Incident AS Incident_TP
INNER JOIN Site AS Site_TP ON Incident_TP.SiteID = Site_TP.SiteID
INNER JOIN Region AS Region_TP ON Site_TP.RegionID = Region_TP.RegionID
WHERE Incident_TP.TotalLoss > 0.00
),
TopFiveSetA
AS ( SELECT TOP 5
*
FROM RawData
WHERE IncidentHappenedDate_TP BETWEEN #IncidentFromDate_TP
AND #IncidentToDate_TP
ORDER BY TotalLoss_TP DESC ,
IncidentHappenedDate_TP DESC
),
TopFiveSetB
AS ( SELECT TOP 5
*
FROM RawData
WHERE IncidentHappenedDate_TP BETWEEN #IncidentFromDate_LP
AND #IncidentToDate_LP
ORDER BY TotalLoss_TP DESC ,
IncidentHappenedDate_TP DESC
),
Merged
AS ( SELECT *
FROM TopFiveSetA
UNION ALL
SELECT *
FROM TopFiveSetB
)
SELECT *
FROM Merged
SELECT DISTINCT f.FoodNumber,
f.FoodID,
fn.Name AS FoodName,
d.[Description],
substring(n.Note, CHARINDEX(']', n.Note) + 2,
LEN(n.Note)) AS FoodNote
FROM Food f
JOIN FoodName fn
ON fn.FoodNameID = f.FoodNameID
JOIN FoodPart fp
ON fp.FoodID = p.FoodID
JOIN [Application] a
ON a.ApplicationID = fn.ApplicationID
LEFT JOIN [Description] d
ON d.DescriptionID = ap.DescriptionID
JOIN Note n
ON n.NoteID = a.NoteID
JOIN FoodYear fy
ON fy.FoodYearID = a.FoodYearID
WHERE mmy.FoodlId = 33997332
ORDER BY CASE
WHEN substring(n.Note, 1, 1) = 'A' THEN 1
WHEN substring(n.Note, 1, 1) = 'Y' THEN 2
WHEN substring(n.Note, 1, 1) = 'D ' THEN 3
END,
f.FoodNumber,
f.FoodID,
fn.Name,
d.[description],
substring(n.Note, CHARINDEX(']', n.Note) + 2, LEN(n.Note))
I keep getting an error saying not all the items in the order by are in the select list.
UPDATE
This works but I'm getting dup part numbers listed...I can't add distinct or it will complain that I don't have something in the select list for the order by
select ap.applicationID,
ap.NoteID,
f.FoodNumber,
n.Note as PartNote,
f.FoodID,
q.Quantity,
fn.Name as FoodName,
d.[Description]
from Food f
join FoodName fn on fn.FoodNameID = f.FoodNameID
join FoodPart fp on fp.partID = f.FoodID
join Quantity q on q.QuantityID = ap.QuantityID
join [Application] a on a.ApplicationID = ap.ApplicationID
left join [Description] d on d.DescriptionID = ap.DescriptionID
join Note n on n.NoteID = ap.NoteID
join Note n2 on n2.NoteID = a.NoteID
join FoodYear fy on fy.FoodYearID = a.FoodYearID
join Model mo on mo.ModelID = fy.ModelID
where fy.ModelId = #ModelId
order by
case when substring(f.FoodNumber, 1, 1) = 'T' then 1
when substring(f.FoodNumber, 1, 1) = 'R' then 2
when substring(f.FoodNumber, 1, 1) = 'C' then 3
else
substring(f.FoodNumber, 1, 1)
END,
f.FoodNumber asc,
f.FoodID,
fn.Name ,
d.[description],
substring(n.Note, CHARINDEX(']', n.Note) + 2, LEN(n.Note))
You need to add SUBSTRING(n.Note, 1, 1) to the SELECT list or remove the DISTINCT.
You currently are doing DISTINCT on SUBSTRING(n.Note, CHARINDEX(']', n.Note) + 2, LEN(n.Note)) AS FoodNote but there can be multiple possible SUBSTRING(n.Note, 1, 1) values that all map to the same value for that.
WITH n(Note) AS
(
SELECT '' UNION ALL
SELECT 'A' UNION ALL
SELECT ']'
)
SELECT SUBSTRING(n.Note, CHARINDEX(']', n.Note) + 2,
LEN(n.Note)) AS FoodNote
,SUBSTRING(n.Note, 1, 1) AS SS
FROM n
Returns
FoodNote SS
-------- ----
A
]
You can also use GROUP BY instead of DISTINCT and ORDER BY MIN(SUBSTRING(n.Note, 1, 1)) so the expression becomes unambiguous in that event and is allowed.
i.e. something like
;WITH CTE AS
(
SELECT
f.FoodNumber,
f.FoodID,
fn.Name AS FoodName,
d.[Description],
substring(n.Note, CHARINDEX(']', n.Note) + 2,
LEN(n.Note)) AS FoodNote,
substring(n.Note, 1, 1) AS N1
FROM Food f
JOIN FoodName fn
ON fn.FoodNameID = f.FoodNameID
JOIN FoodPart fp
ON fp.FoodID = p.FoodID
JOIN [Application] a
ON a.ApplicationID = fn.ApplicationID
LEFT JOIN [Description] d
ON d.DescriptionID = ap.DescriptionID
JOIN Note n
ON n.NoteID = a.NoteID
JOIN FoodYear fy
ON fy.FoodYearID = a.FoodYearID
WHERE mmy.FoodlId = 33997332
)
SELECT FoodNumber,
FoodID,
FoodName,
[Description],
FoodNote
FROM CTE
GROUP BY FoodNumber,
FoodID,
FoodName,
[Description],
FoodNote
ORDER BY CASE
WHEN MIN(N1) = 'A' THEN 1
WHEN MIN(N1) = 'Y' THEN 2
WHEN MIN(N1) = 'D ' THEN 3
END,
FoodNumber,
FoodID,
FoodName,
[Description],
FoodNote
perhaps add
case when when substring(n.Note, 1, 1) = 'A' then 1
when substring(n.Note, 1, 1) = 'Y' then 2
when substring(n.Note, 1, 1) = 'D ' then 3
END,
to your select?
Though I dind't think an order by required something to be in the select.
Also: you don't have a else in the case for if note is not A Y or D.
How would I construct this query using Zend_Db_Select?:
SELECT users.user_id, email_address, t1.value as 'languages'
FROM users
LEFT JOIN (
SELECT
user_id
, field_id
, GROUP_CONCAT(value SEPARATOR ',') AS value
FROM user_multivalued
WHERE field_id=25
GROUP BY user_id, field_id) t1
ON t1.user_id = users.users_id
WHERE list_id = 45
$user_multivalued = $db
->select()
->from('user_multivalued', array(
'user_id',
'field_id',
new Zend_Db_Expr("GROUP_CONCAT(value SEPARATOR ',') AS value")
))
->where('field = ?', 25)
->group('user_id')
->group('field_id')
;
$select = $db
->select()
->from('users', array('user_id', 'email_address'))
->joinLeft(
array('t1' => $user_multivalued),
't1.user_id = users.user_id',
array('languages'=>'value')
)
->where('list_id = ?', 45)
;