I did this query and it works but it is possible to do something more efficient and readable ? I just want to have a result in a query (I don't want to use pl/sql for this). The best would be to store the when condition in a variable but I don't know if it is possible.
SELECT
CASE WHEN (SELECT min(handling_unit_id) FROM ifsapp.handling_unit_shipment where
shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
and structure_level = 1) is not null
THEN (SELECT min(handling_unit_id) FROM ifsapp.handling_unit_shipment where
shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
and structure_level = 1)
ELSE (SELECT min(handling_unit_id) FROM ifsapp.handling_unit_shipment where
shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
and structure_level > 1)
END HUI FROM dual ;
You may use a below query (if you want a single value as in the current example):
select handling_unit_id
from ifsapp.handling_unit_shipment
where shipment_id = '1371'
and ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(
handling_unit_type_id
) like 'SST_N1'
and structure_level >= 1
order by structure_level asc, handling_unit_id asc
fetch first row only
What will it do:
It orders the data by structure_level in ascending order, so given structure_level >= 1 will return structure_level = 1 as a first row if it is present and structure_level > 1 if it is not.
If there are many handling_unit_id per structure value, then it will put the least handling_unit_id at the first row, which is an equivalent of min.
You could make it a little more compact using the coalesce function
SELECT coalesce(
(SELECT min(handling_unit_id) FROM ifsapp.handling_unit_shipment where
shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
and structure_level = 1),
(SELECT min(handling_unit_id) FROM ifsapp.handling_unit_shipment where
shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
and structure_level > 1)
)
FROM dual ;
The coalesce function basically returns the first not null value among its input parameters
You may use query factoring or common-table-expression.
with tb as (
SELECT min(handling_unit_id) min_hu_id
FROM ifsapp.handling_unit_shipment
where shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
and structure_level = 1)
)
select coalesce(min_hu_id
,(SELECT min(handling_unit_id)
FROM ifsapp.handling_unit_shipment
where shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
and structure_level > 1)
from tb
;
Another option. Figure out what is common to all three queries and do it once in a CTE (WITH clause). Then query that in respective ways to look for what is different in each case. Then there are various modes of doing the CASE/IF-THEN type logic. Here's one means. It uses a tricky SIGN operation in order to group all the >1 structure_level values together.
WITH data AS (SELECT SIGN(structure_level-1)+1 structure_level_group,min(handling_unit_id) min_handling_unit_id
FROM ifsapp.handling_unit_shipment
WHERE shipment_id = '1371'
AND ifsapp.HANDLING_UNIT_TYPE_API.Get_Handling_Unit_Category_Id(handling_unit_type_id) like 'SST_N1'
GROUP BY SIGN(structure_level-1)+1)
SELECT NVL(t1.min_handling_unit_id,t2.min_handling_unit_id) hui
FROM (SELECT MIN(min_handling_unit_id) min_handling_unit_id
FROM data
WHERE structure_level_group = 1) t1,
(SELECT MIN(min_handling_unit_id) min_handling_unit_id
FROM data
WHERE structure_level_group = 2) t2
Related
I am trying to get the total count for a specific "COMPONENT_DATA_POINT"."NAME" as it relates to the "DATA_POINT_UPLOAD_DATA"."VALUE". For example, here is my current query:
select "DATA_POINT_UPLOAD_DATA"."VALUE" as "EQUIP_ONLINE_VALUE"
from "DB"."DATA_POINT_UPLOAD_DATA" "DATA_POINT_UPLOAD_DATA"
where
"DATA_POINT_UPLOAD_DATA"."COMPONENT_UPLOAD_DATA_ID" IN (select id from (select "COMPONENT_UPLOAD_DATA".* from "DB"."COMPONENT_UPLOAD_DATA" "COMPONENT_UPLOAD_DATA",
"DB"."COMPONENT" "COMPONENT"
where
"COMPONENT_UPLOAD_DATA"."DATA_COLLECTED_TIME" < CURRENT_DATE
and "COMPONENT_UPLOAD_DATA"."COMPONENT_NO"='1'
and "COMPONENT_UPLOAD_DATA"."SITE_UPLOAD_DATA_SITE_ID"="COMPONENT"."SITE_ID"
and "COMPONENT_UPLOAD_DATA"."COMPONENT_ID"="COMPONENT"."ID"
and "COMPONENT"."COMPONENT_TYPE_ID" = '123'
and "COMPONENT"."SITE_ID" in ('123ABC')
ORDER BY "COMPONENT_UPLOAD_DATA"."DATA_COLLECTED_TIME" DESC)
where rownum <= 1)
and "DATA_POINT_UPLOAD_DATA"."COMPONENT_DATA_POINT_ID" IN (select COMPONENT_DATA_POINT_ID from (select "DATA_POINT_UPLOAD_DATA".*
from "DB"."COMPONENT_DATA_POINT" "COMPONENT_DATA_POINT",
"DB"."DATA_POINT_UPLOAD_DATA" "DATA_POINT_UPLOAD_DATA",
"DB"."COMPONENT" "COMPONENT"
where "DATA_POINT_UPLOAD_DATA"."COMPONENT_DATA_POINT_ID"="COMPONENT_DATA_POINT"."ID"
and "COMPONENT_DATA_POINT"."NAME" ='EquipUp'
and "COMPONENT_DATA_POINT"."COMPONENT_ID" = "COMPONENT"."ID"
and "COMPONENT"."COMPONENT_TYPE_ID" = '123'
and "COMPONENT"."SITE_ID" in ('123ABC')));
Current output I believe only shows one count, and not a total sum of all 'EquipUp':
EQUIP_ONLINE_VALUE
------------------------
1
I would like to get a total 'EQUIP_ONLINE_VALUE' sum of all 'EquipUp' names.
Any help would be greatly appreciated.
Thanks.
You do not have a count / sum function in your query.
Your where clause only returns 1 row, which has a DATA_POINT_UPLOAD_DATA.VALUE
of 1.
Are you sure your rownum <= 1 is correct?
Try running each section in your where clause individually to determine whether they
bring back multiple rows of data.
If you require the sum of each
EquipUp, use a SUM(value) from... group By COMPONENT_DATA_POINT.NAME
Run these 2 scripts to determine you have the correct data to begin with:
SELECT "COMPONENT_UPLOAD_DATA".*
FROM "DB"."COMPONENT_UPLOAD_DATA" "COMPONENT_UPLOAD_DATA"
,"DB"."COMPONENT" "COMPONENT"
WHERE "COMPONENT_UPLOAD_DATA"."DATA_COLLECTED_TIME" < CURRENT_DATE
AND "COMPONENT_UPLOAD_DATA"."COMPONENT_NO" = '1'
AND "COMPONENT_UPLOAD_DATA"."SITE_UPLOAD_DATA_SITE_ID" = "COMPONENT"."SITE_ID"
AND "COMPONENT_UPLOAD_DATA"."COMPONENT_ID" = "COMPONENT"."ID"
AND "COMPONENT"."COMPONENT_TYPE_ID" = '123'
AND "COMPONENT"."SITE_ID" IN ('123ABC')
ORDER BY "COMPONENT_UPLOAD_DATA"."DATA_COLLECTED_TIME" DESC
SELECT "DATA_POINT_UPLOAD_DATA".*
FROM "DB"."COMPONENT_DATA_POINT" "COMPONENT_DATA_POINT"
,"DB"."DATA_POINT_UPLOAD_DATA" "DATA_POINT_UPLOAD_DATA"
,"DB"."COMPONENT" "COMPONENT"
WHERE "DATA_POINT_UPLOAD_DATA"."COMPONENT_DATA_POINT_ID" = "COMPONENT_DATA_POINT"."ID"
AND "COMPONENT_DATA_POINT"."NAME" = 'EquipUp'
AND "COMPONENT_DATA_POINT"."COMPONENT_ID" = "COMPONENT"."ID"
AND "COMPONENT"."COMPONENT_TYPE_ID" = '123'
AND "COMPONENT"."SITE_ID" IN ('123ABC')
The query below accesses the Votes table that contains over 30 million rows. The result set is then selected from using WHERE n = 1. In the query plan, the SORT operation in the ROW_NUMBER() windowed function is 95% of the query's cost and it is taking over 6 minutes to complete execution.
I already have an index on same_voter, eid, country include vid, nid, sid, vote, time_stamp, new to cover the where clause.
Is the most efficient way to correct this to add an index on vid, nid, sid, new DESC, time_stamp DESC or is there an alternative to using the ROW_NUMBER() function for this to achieve the same results in a more efficient manner?
SELECT v.vid, v.nid, v.sid, v.vote, v.time_stamp, v.new, v.eid,
ROW_NUMBER() OVER (
PARTITION BY v.vid, v.nid, v.sid ORDER BY v.new DESC, v.time_stamp DESC) AS n
FROM dbo.Votes v
WHERE v.same_voter <> 1
AND v.eid <= #EId
AND v.eid > (#EId - 5)
AND v.country = #Country
One possible alternative to using ROW_NUMBER():
SELECT
V.vid,
V.nid,
V.sid,
V.vote,
V.time_stamp,
V.new,
V.eid
FROM
dbo.Votes V
LEFT OUTER JOIN dbo.Votes V2 ON
V2.vid = V.vid AND
V2.nid = V.nid AND
V2.sid = V.sid AND
V2.same_voter <> 1 AND
V2.eid <= #EId AND
V2.eid > (#EId - 5) AND
V2.country = #Country AND
(V2.new > V.new OR (V2.new = V.new AND V2.time_stamp > V.time_stamp))
WHERE
V.same_voter <> 1 AND
V.eid <= #EId AND
V.eid > (#EId - 5) AND
V.country = #Country AND
V2.vid IS NULL
The query basically says to get all rows matching your criteria, then join to any other rows that match the same criteria, but which would be ranked higher for the partition based on the new and time_stamp columns. If none are found then this must be the row that you want (it's ranked highest) and if none are found that means that V2.vid will be NULL. I'm assuming that vid otherwise can never be NULL. If it's a NULLable column in your table then you'll need to adjust that last line of the query.
I have tried the below and i am able to get the answer but i need to call this as single query in front end, so can any one an alternative for this.
I don't need this as an SP:
DECLARE #ven INT; Declare #dai int;
select #ven= SUM(po) from Vendortable where VendorName ='HP' and Date = '2014-01-22'
select #dai= SUM(completepo) from Daily_Volume_Tracker where AccountVendorName = 'HP' and date > = '2014-01-22'
select #ven - #dai
Result:
Result
--------
5831
Off the top of my head, something like this might work:
WITH CTE AS
(
select
(select SUM(po) from Vendortable where VendorName ='HP' and Date = '2014-01-22') as Val1
,(select SUM(completepo) from Daily_Volume_Tracker where AccountVendorName = 'HP' and date > = '2014-01-22' as Val2
)
Select Val1 - Val2 From CTE
I love using common table expressions.
Try this simple subquery
Select
(
(
select SUM(po) as Ven from Vendortable where VendorName ='HP' and Date = '2014-01-22'
)
-
(
select SUM(completepo) as Dai from Daily_Volume_Tracker where AccountVendorName = 'HP' and date > = '2014-01-22'
)
) Result
I am new to SQL so bear with me. I am returning data from multiple tables. Followed is my SQL (let me know if there is a better approach):
SELECT [NonScrumStory].[IncidentNumber], [NonScrumStory].[Description], [DailyTaskHours].[ActivityDate], [Application].[AppName], [SupportCatagory].[Catagory], [DailyTaskHours].[PK_DailyTaskHours],n [NonScrumStory].[PK_NonScrumStory]
FROM [NonScrumStory], [DailyTaskHours], [Application], [SupportCatagory]
WHERE ([NonScrumStory].[UserId] = 26)
AND ([NonScrumStory].[PK_NonScrumStory] = [DailyTaskHours].[NonScrumStoryId])
AND ([NonScrumStory].[CatagoryId] = [SupportCatagory].[PK_SupportCatagory])
AND ([NonScrumStory].[ApplicationId] = [Application].[PK_Application])
AND ([NonScrumStory].[Deleted] != 1)
AND [DailyTaskHours].[ActivityDate] >= '1/1/1990'
ORDER BY [DailyTaskHours].[ActivityDate] DESC
This is what is being returned:
This is nearly correct. I only want it to return one copy of PK_NonScrumStory though and I can't figure out how. Essentially, I only want it to return one copy so one of the top two rows would not be returned.
You could group by the NonScrumStore columns, and then aggregate the other columns like this:
SELECT [NonScrumStory].[IncidentNumber],
[NonScrumStory].[Description],
MAX( [DailyTaskHours].[ActivityDate]),
MAX( [Application].[AppName]),
MAX([SupportCatagory].[Catagory]),
MAX([DailyTaskHours].[PK_DailyTaskHours]),
[NonScrumStory].[PK_NonScrumStory]
FROM [NonScrumStory],
[DailyTaskHours],
[Application],
[SupportCatagory]
WHERE ([NonScrumStory].[UserId] = 26)
AND ([NonScrumStory].[PK_NonScrumStory] = [DailyTaskHours].[NonScrumStoryId])
AND ([NonScrumStory].[CatagoryId] = [SupportCatagory].[PK_SupportCatagory])
AND ([NonScrumStory].[ApplicationId] = [Application].[PK_Application])
AND ([NonScrumStory].[Deleted] != 1)
AND [DailyTaskHours].[ActivityDate] >= '1/1/1990'
group by [NonScrumStory].[IncidentNumber], [NonScrumStory].[Description],[NonScrumStory].[PK_NonScrumStory]
ORDER BY 3 DESC
From the screenshot it seems DISTINCT should have solved your issue but if not you could use the ROW_NUMBER function.
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY [NonScrumStory].[PK_NonScrumStory] ORDER BY [DailyTaskHours].[ActivityDate] DESC) AS RowNum,
[NonScrumStory].[IncidentNumber], [NonScrumStory].[Description], [DailyTaskHours].[ActivityDate], [Application].[AppName], [SupportCatagory].[Catagory], [DailyTaskHours].[PK_DailyTaskHours],n [NonScrumStory].[PK_NonScrumStory]
FROM [NonScrumStory], [DailyTaskHours], [Application], [SupportCatagory]
WHERE ([NonScrumStory].[UserId] = 26)
AND ([NonScrumStory].[PK_NonScrumStory] = [DailyTaskHours].[NonScrumStoryId])
AND ([NonScrumStory].[CatagoryId] = [SupportCatagory].[PK_SupportCatagory])
AND ([NonScrumStory].[ApplicationId] = [Application].[PK_Application])
AND ([NonScrumStory].[Deleted] != 1)
AND [DailyTaskHours].[ActivityDate] >= '1/1/1990'
)
SELECT * FROM CTE WHERE RowNum = 1 ORDER BY [ActivityDate] DESC
I believe if you add DISTINCT to your query that should solve your problem. Like so
SELECT DISTINCT [NonScrumStory].[IncidentNumber], [NonScrumStory].[Description],...
here is one query that returns only 1 column called datapath:
SELECT --assumes number not at end of string
LEFT(startOf, PATINDEX('%[^0-9]%', startof)-1)
FROM
(
SELECT --assumed 3 digits minimum
SUBSTRING(datapath, PATINDEX('%[0-9][0-9][0-9]%', datapath), 8000) AS startOf
FROM
(select datapath from batchinfo where LEN(datapath)>3) as bar
) foo
another words in this select statement below, instead of select datapath, i would like to run the above sql statement
SELECT reporttime,
datapath,
finalconc,
instrument
FROM batchinfo
JOIN qvalues ON batchinfo.rowid = qvalues.rowid
WHERE compound = 3 AND name = "hey"
AND batchinfo.instrument = 44
AND batchinfo.reporttime LIKE '10/%/2010%'";
SELECT reporttime,
datapath,
finalconc,
instrument
FROM
(
SELECT --assumes number not at end of string
LEFT(startOf, PATINDEX('%[^0-9]%', startof)-1) AS datapath, --correct?
rowid, instrument , reporttime
FROM
(
SELECT --assumed 3 digits minimum
SUBSTRING(datapath, PATINDEX('%[0-9][0-9][0-9]%', datapath), 8000) AS startOf,
rowid, instrument , reporttime
FROM
batchinfo --don't need LEN check. PATINDEX will do that implicitly
) foo
) batchinfo
JOIN qvalues ON batchinfo.rowid = qvalues.rowid
WHERE compound = 3 AND name = "hey"
AND batchinfo.instrument = 44
AND batchinfo.reporttime LIKE '10/%/2010%'";
If I'm reading this correctly, I would think you should be able assign your 'datapath' column in the second select to the value you're selecting in the first, and replace 'startOf' with the value you're selecting for startOf.
I think it would be something like this:
SELECT
reporttime,
'datapath' = LEFT(SUBSTRING(datapath, PATINDEX('%[0-9][0-9][0-9]%', datapath), 8000), PATINDEX('%[^0-9]%', SUBSTRING(datapath, PATINDEX('%[0-9][0-9][0-9]%', datapath), 8000))-1) ,
finalconc,
instrument
FROM batchinfo
JOIN qvalues ON batchinfo.rowid = qvalues.rowid
WHERE compound = 3 AND name = "hey"
AND batchinfo.instrument = 44
AND batchinfo.reporttime LIKE '10/%/2010%'";