Using MIN() to get the lowest value, but i got two rows? - sql-server

I'm using a SQL question were I want to find the lowest value from the field prod_week.
This is the query:
SELECT
MIN(oe.prod_week), oe.prodplan_id
FROM
pd_mounting_details as md
LEFT OUTER JOIN
pd_order_eco AS oe ON md.order_data = oe.id
LEFT OUTER JOIN
pd_article AS a ON md.article = a.id
WHERE
oe.status = 4
AND (md.starttime = '' OR md.starttime IS NULL)
AND (a.production_group = 4)
AND (NOT (oe.amount = 0))
GROUP BY
oe.prodplan_id
The result of this is
prod_week | prodplan_id
1126 | 27
1127 | 28
What I don't understand is why this result in two rows when I used MIN(prod_week) to get the row with the lowest week number.
If I remove the prodplan_id from the selection it all works and I get one row were prod_week is "1126". And from that all I want is to get the id prodplan_id to.
I hope this question isn't to blurry?

You are using GROUP BY, which means you will get one row per GROUP.
In this case your GROUP is prodplan_id and there are two matching values.
To get both values you can try:
SELECT oe.prod_week, oe.prodplan_id
FROM pd_mounting_details as md
LEFT OUTER JOIN pd_order_eco AS oe
ON md.order_data = oe.id
WHERE oe.prod_week = (SELECT MIN(oe.prod_week)
FROM pd_mounting_details as md
LEFT OUTER JOIN pd_order_eco AS oe
ON md.order_data = oe.id
LEFT OUTER JOIN pd_article AS a
ON md.article = a.id where oe.status=4
AND (md.starttime ='' or md.starttime is null)
AND (a.production_group = 4)
AND (NOT (oe.amount = 0)))

When you do
select min(x),y
from table
group by y;
what you're doing is getting y and the smallest value of x for each distinct value of y. So, since prodplan_id has values of 27 and 28 in your morass of joins, we have that the smallest value of prod_week that appears when prodplan_id=27 is 1126, and the smallest value of prod_week that appears when prodplan_id=28 is 1127.
ETA: If you want one row, you could do an order by 1 limit 1 at the end.
ETA^2: You can also wrap things up in a subquery and use a where clause at the end:
select min_prod_week,prodplan_id
from(
select min(oe.prod_week) as min_prod_week,oe.prodplan_id
from....
group by oe.prodplan_id
)min
where min_prod_week=(select min(prod_week) from pd_order_eco)

Since your select statement ends with a group by clause, you are selecting the minimum prod_week for each prodplan_id instead of the overall minimum. Remove the group by clause and it should work as you expect.

Related

SQL Server Sum Returning Invalid Number

I am using LEFT JOIN three times in a query to display user information. For some reason, the second last LEFT JOIN affects the output of the SUM function.
I have tried removing the second last LEFT JOIN statement which returns the correct value. I don't see why it would change the value.
SELECT
[tbl_users].[id],
[username],
COUNT([tbl_password_resets].[id]) as passwordresets,
SUM(CASE WHEN [tbl_files].[user_id] = [tbl_users].[id] THEN 1 ELSE 0 END) as uploads,
COUNT([tbl_downloads].[id]) as downloads,
CAST(SUM(CASE WHEN [tbl_downloads].[liked] = 1 OR [tbl_downloads].[disliked] = 1 THEN 1 ELSE 0 END) AS FLOAT) / NULLIF(COUNT([tbl_downloads].[id]), 0) as ratio,
[ban]
FROM
[tbl_users]
LEFT JOIN
[tbl_password_resets] ON [tbl_users].[id] = [tbl_password_resets].[user_id]
LEFT JOIN
[tbl_downloads] ON [tbl_users].[id] = [tbl_downloads].[user_id]
LEFT JOIN
[tbl_files] ON [tbl_files].[user_id] = [tbl_users].[id]
GROUP BY
[tbl_users].[id], [tbl_users].[username], [tbl_users].[ban]
The result for uploads is 9 instead of 3.
If any joined table is duplicating rows, that affects the result of COUNTs and SUMs. Comment out the aggregates and the GROUP BY and the JOINs for testing and see what happens with the row count when you build the query adding the JOINs one-by-one. The more rows fall into one group, the more values will be COUNTed and SUMmed.

Conditional subquery that returns a running total

I am trying to run a subquery with a condition that returns a running total. However, I am receiving the following error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
Is there any way this code can be salvaged? Please be aware this code is part of a larger script that executes perfectly. The reason I need to keep it in this format is because it is the "missing piece", for lack of a better word.
SELECT A.[WeekEnding],
(
SELECT SUM(A.[Weekly Sales Units]), A.[Description], A.[WeekEnding]
FROM [FACT_SALES_HISTORY] A
INNER JOIN [DIM_DATE] B
ON A.WeekEnding = B.[WeekEnding] WHERE B.[YA Latest 1 Week] = 1
GROUP BY A.[Description], A.[WeekEnding]
) AS 'YA Units'
FROM [FACT_SALES_HISTORY] A
LEFT JOIN [DIM_DATE] B
ON A.WeekEnding = B.[WeekEnding]
The output data, from the code, would look like the following:
[Weekly Sales Units]) A.[Description] A.[WeekEnding]
24 Item One 03-10-2010
55 Item Two 03-10-2010
79 Item One 03-10-2010
98 Item Five 03-10-2010
11 Item Five 03-10-2010
You can't select three different items in your subquery and then use an AS assignment. You could split that into two separate queries and then union them.
SELECT SUM(A.[Weekly Sales Units]), A.[Description], A.[WeekEnding]
FROM [FACT_SALES_HISTORY] A
INNER JOIN [DIM_DATE] B
ON A.WeekEnding = B.[WeekEnding] WHERE B.[YA Latest 1 Week] = 1
GROUP BY A.[Description], A.[WeekEnding]
UNION ALL -- This will only union distinct columns
SELECT A.[WeekEnding]...<Your other columns>
FROM [FACT_SALES_HISTORY] A
t looks like your subquery on its own would provide the sample data and the outer query is trying to sum that up by weekending. If that is the case, then the whole thing could be replaced with this:
SELECT A.[WeekEnding], SUM(A.[Weekly Sales Units]) [YA Units]
FROM [FACT_SALES_HISTORY] A
INNER JOIN [DIM_DATE] B
ON A.WeekEnding = B.[WeekEnding] WHERE B.[YA Latest 1 Week] = 1
GROUP BY A.[WeekEnding]

Althought I specify one expression sql gives "Only one expression can be specified error"

DS.SOURCE_TYPE,
(SELECT
I_MAX_VADE,
CASE
WHEN (PA.ACTOR_KIND = 5 AND PA.PROCESS_ID = PROCESS_ID)
THEN 0
ELSE I_MAX_VADE
END
FROM
KDS_INTER_FAKTORING_OMDM_PARAMS
WHERE
PA.ID = PROCESS_ACTOR_ID) AS TERER
FROM
dbo.PROCESS_ACTOR AS PA
JOIN
dbo.OMDM_RESULT AS O ON O.PROCESS_ID = PA.PROCESS_ID
JOIN
dbo.KDS_PROPOSAL_OMDM_PARAMS AS POP ON POP.PROCESS_ID = PA.PROCESS_ID
JOIN
dbo.PROPOSAL_SNAP AS PS ON PS.PROCESS_ID = PA.PROCESS_ID
JOIN
dbo.DEBTOR_SNAP AS DS ON DS.PROCESS_ID = PA.PROCESS_ID
This simple query throws the error specified below.
I think I have already selected one column in my subquery. I would like to assign I_MAX_VADE to its db value.
if (PA.ACTOR_KIND = 5 AND PA.PROCESS_ID = PROCESS_ID) else zero.
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
Your subquery select returns two columns: I_MAX_VADE and the result of the CASE expression. That is one too many.
Use OUTER APPLY. It is like a correlated subquery (sometimes) but it is in the FROM clause rather than the SELECT. And, you can have as many columns as you like:
SELECT . . .,
DS.SOURCE_TYPE,
params.*
FROM dbo.PROCESS_ACTOR PA JOIN
dbo.OMDM_RESULT O
ON O.PROCESS_ID = PA.PROCESS_ID JOIN
dbo.KDS_PROPOSAL_OMDM_PARAMS POP
ON POP.PROCESS_ID = PA.PROCESS_ID JOIN
dbo.PROPOSAL_SNAP PS
ON PS.PROCESS_ID = PA.PROCESS_ID JOIN
dbo.DEBTOR_SNAP DS
ON DS.PROCESS_ID = PA.PROCESS_ID OUTER APPLY
(SELECT I_MAX_VADE,
(CASE WHEN PA.ACTOR_KIND = 5 AND PA.PROCESS_ID = params.PROCESS_ID THEN 0
ELSE params.I_MAX_VADE
END) as TERER
FROM KDS_INTER_FAKTORING_OMDM_PARAMS params
WHERE PA.ID = params.PROCESS_ACTOR_ID
) params
Note: I had to guess at the source of a couple columns. You should always qualify column names -- and this is even more important when you are using correlated subqueries.
+1 to answer from HABO, but I'll add an example:
WRONG:
SELECT A, B, (SELECT X, Y FROM MyTable) AS C
FROM OtherTable
When you put a subquery into your select-list, the subquery must be a scalar subquery; i.e. it must be guaranteed to return one column and one row.
The example above is wrong because the subquery returns two columns, and it is not necessarily going to return a single row.
RIGHT:
SELECT A, B, (SELECT TOP 1 X FROM MyTable) AS C
FROM OtherTable
I finally figure out how I can accomplish my task.Here is my updated code.
I simply remove my column(I_MAX_VADE) from select case and insert it into then clause.
**
Before
**
(SELECT
I_MAX_VADE,
CASE
WHEN (PA.ACTOR_KIND = 5 AND PA.PROCESS_ID = PROCESS_ID)
THEN 0
ELSE I_MAX_VADE
END
FROM
KDS_INTER_FAKTORING_OMDM_PARAMS
WHERE
PA.ID = PROCESS_ACTOR_ID) AS TERER
**
After
**
(SELECT
(CASE
WHEN (PA.ACTOR_KIND=5)
THEN (SELECT I_MAX_VADE FROM KDS_INTER_FAKTORING_OMDM_PARAMS
WHERE PA.ID=PROCESS_ACTOR_ID )
ELSE 0 END) ) AS TERER

How to display only the MAX results from a query

I am new to writing MS SQL queries and I am trying to display only the record with the highest field named RecordVersion.
Below is the query that works but displays all records:
SELECT
PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
Here are the query results:
PriceProgramID EffectiveDateTime Price PLU Descr LastUpdate LastUpdatedBy RecordVersion PriceScheduleUniqueID
1 2016-03-22 00:00:00.000 35.00 SLS10100103 Architecture Adult from NP POS 2015-01-22 07:53:15.000 GX70,83 9 569
1 2016-03-22 00:00:00.000 32.00 SLS10100103 Architecture Adult from NP POS 2014-02-25 16:22:46.000 GX70,83 5 86180
The first line of the results has RecordVersion being 9 and the second line results is 5, I only want the higher record displaying, the one that returned RecordVersion = 9.
Every time I try to use the MAX command I get errors or the group by and I have tried every example I could find on the web but nothing seems to work.
Using MS SQL 2012.
Thanks,
Ken
Try the following query which attempts to solve your problem by ordering the returned rows by RecordVersion DESC and then SELECTs just the first row.
SELECT TOP 1
PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
ORDER BY
RecordVersion DESC
All group by columns should be in select ,that's the rule of group by.How group by works is for every distinct combination of group by columns,arrange remaining columns into groups,so that any aggregation can be applied,in your case I am not sure what group by columns are unique with out test date.here is one version which use row number which gives you the output desired
Remember ,order by last updated date is the one which decides rows order and assign numbers
WITH CTE
AS
(
SELECT PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID,
ROW_NUMBER() OVER (PARTITION BY PriceSchedules.RecordVersion ORDER BY PriceSchedules.LastUpdatedBy) AS RN
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
)
SELECT * FROM CTE WHERE RN=1

How to join one select with another when the first one not always returns a value for specific row?

I have a complex query to retrieve some results:
EDITED QUERY (added the UNION ALL):
SELECT t.*
FROM (
SELECT
dbo.Intervencao.INT_Processo, analista,
ETS.ETS_Sigla, ATC.ATC_Sigla, PAT.PAT_Sigla, dbo.Assunto.SNT_Peso,
CASE
WHEN ETS.ETS_Sigla = 'PE' AND (PAT.PAT_Sigla = 'LIB' OR PAT.PAT_Sigla = 'LBR') THEN (0.3*SNT_Peso)
WHEN ETS.ETS_Sigla = 'CD' THEN (0.3*SNT_Peso)*0.3
ELSE SNT_Peso
END AS PESOAREA,
CASE
WHEN a.max_TEA_FimTarefa IS NULL THEN a.max_TEA_InicioTarefa
ELSE a.max_TEA_FimTarefa
END AS DATA_INICIO_TERMINO,
ROW_NUMBER() OVER (PARTITION BY ATC.ATC_Sigla, a.SRV_Id ORDER BY TEA_FimTarefa DESC) AS seqnum
FROM dbo.Tarefa AS t
INNER JOIN (
SELECT
MAX(dbo.TarefaEtapaAreaTecnica.TEA_InicioTarefa) AS max_TEA_InicioTarefa,
MAX (dbo.TarefaEtapaAreaTecnica.TEA_FimTarefa) AS max_TEA_FimTarefa,
dbo.Pessoa.PFJ_Descri AS analista, dbo.AreaTecnica.ATC_Id, dbo.Tarefa.SRV_Id
FROM dbo.TarefaEtapaAreaTecnica
LEFT JOIN dbo.Tarefa ON dbo.TarefaEtapaAreaTecnica.TRF_Id = dbo.Tarefa.TRF_Id
LEFT JOIN dbo.AreaTecnica ON dbo.TarefaEtapaAreaTecnica.ATC_Id = dbo.AreaTecnica.ATC_Id
LEFT JOIN dbo.ServicoAreaTecnica ON dbo.TarefaEtapaAreaTecnica.ATC_Id = dbo.ServicoAreaTecnica.ATC_Id
AND dbo.Tarefa.SRV_Id = dbo.ServicoAreaTecnica.SRV_Id
INNER JOIN dbo.Pessoa ON dbo.Pessoa.PFJ_Id = dbo.ServicoAreaTecnica.PFJ_Id_Analista
GROUP BY dbo.AreaTecnica.ATC_Id, dbo.Tarefa.SRV_Id, dbo.Pessoa.PFJ_Descri
) AS a ON t.SRV_Id = a.SRV_Id
INNER JOIN dbo.TarefaEtapaAreaTecnica AS TarefaEtapaAreaTecnica_1 ON
t.TRF_Id = TarefaEtapaAreaTecnica_1.TRF_Id
AND a.ATC_Id = TarefaEtapaAreaTecnica_1.ATC_Id
AND a.max_TEA_InicioTarefa = TarefaEtapaAreaTecnica_1.TEA_InicioTarefa
LEFT JOIN AreaTecnica ATC ON TarefaEtapaAreaTecnica_1.ATC_Id = ATC.ATC_Id
LEFT JOIN Etapa ETS ON TarefaEtapaAreaTecnica_1.ETS_Id = ETS.ETS_Id
LEFT JOIN ParecerTipo PAT ON TarefaEtapaAreaTecnica_1.PAT_Id = PAT.PAT_Id
LEFT OUTER JOIN dbo.Servico ON a.SRV_Id = dbo.Servico.SRV_Id
INNER JOIN dbo.Intervencao ON dbo.Servico.INT_Id = dbo.Intervencao.INT_Id
LEFT JOIN dbo.Assunto ON dbo.Servico.SNT_Id = dbo.Assunto.SNT_Id
) t
The result is following:
It works good, the problem is that I was asked that if when a row is not present on this query, it must contain values from another table (ServicoAreaTecnica), so I got this query for the other table based on crucial information of the first query. So if I UNION ALL I get this:
Query1 +
UNION ALL
SELECT INN.INT_Processo,
PES.PFJ_Descri,
NULL, --ETS.ETS_Sigla,
ART.ATC_Sigla,
NULL ,--PAT.PAT_Sigla,
ASS.SNT_Peso,
NULL, --PESOAREA
NULL, --DATA_INICIO_TERMINO
NULL --seqnum
FROM dbo.ServicoAreaTecnica AS SAT
INNER JOIN dbo.AreaTecnica AS ART ON ART.ATC_Id = SAT.ATC_Id
INNER JOIN dbo.Servico AS SER ON SER.SRV_Id = SAT.SRV_Id
INNER JOIN dbo.Assunto AS ASS ON ASS.SNT_Id = SER.SNT_Id
INNER JOIN dbo.Intervencao AS INN ON INN.INT_Id = SER.INT_Id
INNER JOIN dbo.Pessoa AS PES ON PES.PFJ_Id = SAT.PFJ_Id_Analista
The result is following:
So what I want to do is to remove row number 1 because row number 2 exists on the first query, I think I got it explained better this time. The result should be only row number 1, row number 2 would appear only if query 1 doesn't retrieve a row for that particular INN.INT_Processo.
Thanks!
Ok, there are two ways to reduce your record set. Given that you've already written the code to produce the table with the extra rows, it might be easiest to just add code to reduce that:
Select * from
(Select *
, Row_Number() over
(partition by IntProcesso, Analista order by ISNULL(seqnum, 0) desc) as RN
from MyResults) a
where RN = 1
This will assign row_number 1 to any rows that came from your first query, or to any rows from the second query that do not have matches in the first query, then filter out extra rows.
You could also use outer joins with isnull or coalesce, as others have suggested. Something like this:
Select ISNULL(a.IntProcesso, b.IntProcesso) as IntProcesso
, ISNULL(a.Analista, b.Analista) as Analista
, ISNULL(a.ETSsigla, b.ETSsigla) as ETSsigla
[repeat for the rest of your columns]
from Table1 a
full outer join Table2 b
on a.IntProcesso = b.IntProcesso and a.Analista = b.Analista
Your code is hard to read, because of the lengthy names of everything (and to be honest, the fact that they're in a language I don't speak also makes it a lot harder).
But how about: replacing your INNER JOINs with LEFT JOINs, adding more LEFT JOINs to draw in the alternative tables, and introducing ISNULL clauses for each variable you want in the results?
If you do something like ... Query1 Right Join Query2 On ... that should get only the rows in Query2 that don't appear in Query 1.

Resources