Add a IF CASE or something similar inside a query - sql

I have a query result, but there is a field that contains the "PROJECT NUMBER", that appears twice, and where there's supposed to be a COUNT of all the samples, the count appears twice. Any idea how can I solve this?
Update:
I can now show the table data structure
Here's more or less the info of the tables:
SAMPLE table
+---------------+--------------+--------+--------------+--------------+
| sample_number | project | status | template | parent_aliquot|
+---------------+--------------+--------------+----------+-------------+
| 1 | S/180318/01 | C | KPS_DEFAULT | 50 |
| 2 | S/180320/01 | I | KPS_DEFAULT | 100 |
+---------------+--------------+--------+--------------+---------------+
enter image description here
KPS_SMP_DUE_DATE_WEEK_PIVOT_VW table (this table shows the amount of samples of a project that have expired in a year)
+---------------+--------------+----------+-------------+----+----+-----+-----+
| project | product | year | department | w0 | w1 | ... | w53 |
+---------------+--------------+----------+-------------+----+----+-----+-----+
| S/000260/02 | Product v1 | 2020 | MICRO | 1 | | | 1 |
| S/180146/04 | Product v2 | 2021 | QC | | 2 | | 3 |
+---------------+--------------+----------+-------------+----+----+-----+-----+
Here's the code that I have at the moment:
Select
temp.PROJECT,
temp.CUSTOMER_DESC,
temp.PRODUCT,
ISNULL(temp.PREVIOUS_SAMPLES,0),
ISNULL(temp.W3,0) as 'W3',
ISNULL(temp.W4,0) as 'W4',
ISNULL(temp.W5,0) as 'W5',
ISNULL(temp.W6,0) as 'W6',
sum(
ISNULL(temp.PREVIOUS_SAMPLES,0)
+ ISNULL(temp.W3,0)
+ ISNULL(temp.W4,0)
+ ISNULL(temp.W5,0)
+ ISNULL(temp.W6,0)
) as 'TOTAL'
from (
SELECT
vw.PROJECT,
vw.CUSTOMER_DESC,
vw.PRODUCT,
(
SELECT COUNT(s.SAMPLE_NUMBER)
FROM SAMPLE s
WHERE s.PROJECT = vw.PROJECT
AND s.STATUS IN ('I', 'U', 'P')
and s.TEMPLATE = 'KPS_DEFAULT'
AND s.PARENT_ALIQUOT > 0
) as 'PREVIOUS_SAMPLES',
ISNULL(vw.W3,0) as 'W3',
ISNULL(vw.W4,0) as 'W4',
ISNULL(vw.W5,0) as 'W5',
ISNULL(vw.W6,0) as 'W6'
FROM KPS_SMP_DUE_DATE_WEEK_PIVOT_VW vw
WHERE vw.DEPARTMENT IN ('QC')
GROUP BY
vw.PROJECT,
vw.CUSTOMER_DESC,
vw.PRODUCT,
vw.W3,
vw.W4,
vw.W5,
vw.W6
) as TEMP
group by
temp.PROJECT,
temp.CUSTOMER_DESC,
temp.PRODUCT,
temp.PREVIOUS_SAMPLES,
temp.W3,
temp.W4,
temp.W5,
temp.W6
UNION
Select
'Total',
'-',
'-',
sum(ISNULL(temp.PREVIOUS_SAMPLES,0) ),
sum( ISNULL(temp.W3,0) ),
sum( ISNULL(temp.W4,0) ),
sum( ISNULL(temp.W5,0) ),
sum( ISNULL(temp.W6,0) ),
sum(
ISNULL(temp.PREVIOUS_SAMPLES,0)
+ ISNULL(temp.W3,0)
+ ISNULL(temp.W4,0)
+ ISNULL(temp.W5,0)
+ ISNULL(temp.W6,0)
)
from (
SELECT
vw.PROJECT,
vw.CUSTOMER_DESC,
vw.PRODUCT,
(
SELECT COUNT(s.SAMPLE_NUMBER)
FROM SAMPLE s
WHERE s.PROJECT = vw.PROJECT
AND s.STATUS IN ('I', 'U', 'P')
and s.TEMPLATE = 'KPS_DEFAULT'
AND s.PARENT_ALIQUOT > 0
) as 'PREVIOUS_SAMPLES',
ISNULL(vw.W3,0) as 'W3',
ISNULL(vw.W4,0) as 'W4',
ISNULL(vw.W5,0) as 'W5',
ISNULL(vw.W6,0) as 'W6'
FROM KPS_SMP_DUE_DATE_WEEK_PIVOT_VW vw
WHERE vw.DEPARTMENT IN ('QC')
GROUP BY
vw.PROJECT,
vw.CUSTOMER_DESC,
vw.PRODUCT,
vw.W3,
vw.W4,
vw.W5,
vw.W6
) as TEMP
As seen on the image, I marked the repeated project.

Your query is way to complicated for anyone else to really figure out. I would suggest that you simplify it -- and in the process you will probably find the issue.
I can say that your question claims that you want one row per project. However, that is not what the code does.
Your GROUP BY is:
group by
temp.PROJECT,
temp.CUSTOMER_DESC,
temp.PRODUCT,
temp.PREVIOUS_SAMPLES,
temp.W3,
temp.W4,
temp.W5,
temp.W6
This says that you want one row for each unique combination of those values. If you want one row per project, I assume you would simply have:
group by temp.PROJECT
Of course, the rest of the code would need to be adjusted to handle this.

Related

How to select company which have two groups

I still tried select all customers which is in two group. Duplicate from customers is normal because select is from invoice but I need to know the customers who had a group in the first half year and jumped to another in the second half year.
Example:
SELECT
f.eankod as kod, --(groups)
ad.kod as firma, --(markComp)
f.nazfirmy as nazev, --(nameComp)
COUNT(ad.kod),
sum(f.sumZklZakl + f.sumZklSniz + f.sumOsv) as cena_bez_dph --(Price)
FROM
ddoklfak as f
LEFT OUTER JOIN aadresar ad ON ad.idfirmy = f.idfirmy
WHERE
f.datvyst >= '2017-01-01'
and f.datvyst <= '2017-12-31'
and f.modul like 'FAV'
GROUP BY
f.eankod,
ad.kod,
f.nazfirmy
HAVING COUNT (ad.kod) > 1
order by
ad.kod
Result:
GROUP markcomp nameComp price
| D002 | B5846 | Cosmopolis | price ... |
| D003 | B6987 | Tismotis | price ... |
| D009 | B8974 | Teramis | price ... |
| D006 | B8876 | Kesmethis | price ... | I need this, same company but diferent group, because this
| D008 | B8876 | Kesmethis | price ... | company jumped. I need know only jumped company. (last two rows from examples)
Thx for help.
You can use a CTE to find out which nameComp show up multiple times, and keep those ones only. For example:
with
x as (
-- your query
)
select * from x where nameComp in (
select nameComp from x group by nameComp having count(*) > 1
)

How to integrate over segments using SQL

I have a table with columns t_b; t_e; x were [t_b, t_e) denotes a period during which x resources where used. I want to compute a table were for each hour h I have amount of resources that where used during [h, h+1) period.
So far my only idea was to generate multiple rows from each input row for each hour (I use an extension of SQL with UDFs) and then simply group by by hour, but I'm afraid this may be too slow considering large amount of data at hand.
Say for example I have a table with two rows:
+-----+-----+---+
| t_b | t_e | x |
+-----+-----+---+
| 1 | 3.5 | a |
| 0.5 | 4 | b |
+-----+-----+---+
Then resulting table should be:
+---+-------------+
| h | x |
+---+-------------+
| 0 | 0*a + 0.5*b |
| 1 | 1*a + 1*b |
| 2 | 1*a + 1*b |
| 3 | 0.5*a + 1*b |
+---+-------------+
You can have a trigger on insert into the stats table that also adds to the aggregate table (the per-hour sums).
If you also need to convert the existing data, you need to run over every row of your current table, split it into amounts/hours and add to the aggregate table.
This is an sql-server example for all number columns
with h as (
-- your hours tally here
select top(24) row_number() over(order by (select null)) eoh from sys.all_objects
), myTable as (
select 1 t_b, 3.5 t_e, 20 v union all
select 0.5, 4, 40
)
select eoh-1 h_starth
, sum(v * (case when t_e < eoh then t_e else eoh end - case when t_b > eoh-1 then t_b else eoh-1 end)) usage
from h
left join myTable t on t_e > eoh - 1 and eoh > t_b -- [..) intresection with [..)
group by eoh;
Fiddle

Merging two tables with some business logic for overfill

I have a bit of a challenge ahead of me with a report I need to write.
I have an ordered selected list of results, which has the following:
+---------+----------+----------+
| Header | estimate | TargetId |
+---------+----------+----------+
| Task 1 | 80 | 1 |
| Task 2 | 30 | 1 |
| Task 3 | 40 | 2 |
| Task 4 | 10 | 2 |
+---------+----------+----------+
I’d like to join this onto another set of data containing the Target information:
+--------+----------+
| Target | Capacity |
+--------+----------+
| 1 | 100 |
| 2 | 50 |
| 3 | 50 |
+--------+----------+
However I’d like to do some sort of pivot / cross join to fill each target to capacity and report this in a way to show a forecast of when each of the tasks for the target will be met.
+---------+----------+----------+----------+----------+---+---+
| Header | Overfill | Target 1 | Target 2 | Target 3 | … | … |
+---------+----------+----------+----------+----------+---+---+
| Task 1 | No | 80 | 0 | 0 | 0 | 0 |
| Task 2 | Yes | 20 | 10 | 0 | 0 | 0 |
| Task 3 | No | 0 | 40 | 0 | 0 | 0 |
| Task 4 | Yes | 0 | 0 | 10 | 0 | 0 |
+---------+----------+----------+----------+----------+---+---+
Alternatively displayed:
+---------+--------+-----------+
| Header | Target | Overfill% |
+---------+--------+-----------+
| Task 1 | 1 | 0 |
| Task 2 | 1,2 | 33.33 |
| Task 3 | 2 | 0 |
| Task 4 | 3 | 100% |
+---------+--------+-----------+
The actual set of data will involve a few hundred tasks across 20 – 30 targets, unfortunately I don’t have any code to show as a demonstration, short of the few simple selects, as I’m not sure how to approach the overfill.
I believe this could be achieved through C# easier however I was hoping this could be completed as a pure SP operation so I can return the data as I wish to display it.
Any help or a nudge in the right direction to take would be greatly appreciated,
Chris
Doing this in SQL is a bad idea, but it is possible with a recursive CTE. The solution below uses a recursive CTE with a result set that maintains the state of the solution as it goes. It queries one record for each source for each recursive iteration and updates the state with the results of certain calculations. Depending on the state in the result it will either advance the sequence, the target, or both.
This solution assumes the targets and headers are sequentially ordered. If the targets aren't sequentially ordered, you can use a CTE to add ROW_NUMBER() to targets. Also if you have more than 32767 steps in the solution it will fail as that is the max recursion that sql server supports. Steps should be at most tasks + targets.
One nice thing is that it will handle overflow across multiple targets. For example, if a task has an estimate it that will fill up multiple targets, then the next task will start at the next available bucket, not the assigned one. Go ahead and put some crazy numbers in there.
Finally, I didn't know how you were deriving overflow percentage, I don't know how you got the last row's result from your sample data. I doubt whatever the answer should be would be difficult to derive once the criteria is known.
/** Setup Test Data **/
DECLARE #Tasks TABLE ( Header VARCHAR(20), Estimate INT, TargetId INT );
DECLARE #Targets TABLE ( TargetId INT, Capacity INT );
INSERT INTO #Tasks VALUES
( 'Task 1', 80, 1 ), ( 'Task 2', 30, 1 ), ( 'Task 3', 40, 2 ), ( 'Task 4', 10, 2 );
INSERT INTO #Targets VALUES ( 1, 100 ), ( 2, 50 ), ( 3, 50 );
/** Solution **/
WITH Sequenced AS (
-- Added SequenceId for tasks as it feels janky to order by headers.
SELECT CAST(ROW_NUMBER() OVER (ORDER BY Header) AS INT) [SequenceId], tsk.*
FROM #Tasks tsk
)
, TargetsWithOverflow AS (
SELECT *
FROM #Targets
UNION
SELECT MAX(TargetId) + 1, 99999999 -- overflow target to store excess not handled by targets
FROM #Targets
)
, src AS (
-- intialize state
SELECT 0 [SequenceId], CAST('' AS varchar(20)) [Header], 0 [Estimate], 0 [CurrentTargetId]
, 0 [CurrentTargetFillLevel], 0 [SequenceRemainingEstimate], 0 [OverfillAmt]
UNION ALL
SELECT seq.SequenceId, seq.header, seq.Estimate, tgt.TargetId
, CASE WHEN [Excess] <= 0 THEN TrueFillLevel + TrueEstimate -- capacity meets estimate
ELSE tgt.Capacity -- there is excess estimate
END
, CASE WHEN [Excess] <= 0 THEN 0 -- task complete
ELSE [Excess] -- task is not complete still some of estimate is left
END
, CASE WHEN tgt.TargetId != seq.TargetId THEN
CASE WHEN [Excess] > 0 THEN [TrueEstimate] - [Excess] ELSE [TrueEstimate] END
ELSE 0
END
FROM src
INNER JOIN Sequenced seq ON
(src.SequenceRemainingEstimate = 0 AND seq.SequenceId = src.SequenceId + 1)
OR (src.SequenceRemainingEstimate > 0 AND seq.SequenceId = src.SequenceId)
INNER JOIN TargetsWithOverflow tgt ON
-- Part of target selection is based on if the sequence advanced.
-- If the sequence has advanced then get the target assigned to the sequence
-- Or use the current one if it is GTE to the assigned target.
-- Otherwise get the target after current target.
(tgt.TargetId = seq.TargetId AND tgt.TargetId > src.CurrentTargetId AND seq.SequenceId != src.SequenceId)
OR (tgt.TargetId = src.CurrentTargetId AND tgt.Capacity >= src.CurrentTargetFillLevel AND seq.SequenceId != src.SequenceId)
OR (tgt.TargetId = src.CurrentTargetId + 1 AND seq.SequenceId = src.SequenceId)
CROSS APPLY (
SELECT CASE WHEN tgt.TargetId != src.CurrentTargetId THEN 0 ELSE src.CurrentTargetFillLevel END [TrueFillLevel]
) forFillLevel
CROSS APPLY (
SELECT tgt.Capacity - [TrueFillLevel] [TrueCapacity]
) forCapacity
CROSS APPLY (
SELECT CASE WHEN src.SequenceRemainingEstimate > 0 THEN src.SequenceRemainingEstimate ELSE seq.Estimate END [TrueEstimate]
) forEstimate
CROSS APPLY (
SELECT TrueEstimate - TrueCapacity [Excess]
) forExcess
)
SELECT src.Header
, LEFT(STUFF((SELECT ',' + RTRIM(srcIn.CurrentTargetId)
FROM src srcIn
WHERE srcIn.Header = src.Header
ORDER BY srcIn.CurrentTargetId
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'), 1, 1, ''), 500)
[Target]
, CASE WHEN SUM(OverfillAmt) > 0 THEN 'Yes' ELSE 'No' END [Overfill]
, SUM (OverfillAmt) / (1.0 * AVG(seq.Estimate)) [OverfillPct]
FROM src
INNER JOIN Sequenced seq ON seq.SequenceId = src.SequenceId
WHERE src.SequenceId != 0
GROUP BY src.Header
OPTION (MAXRECURSION 32767)
Output
Header Target Overfill OverfillPct
-------------------- ---------- -------- ----------------
Task 1 1 No 0.00000000000000
Task 2 1,2 Yes 0.33333333333333
Task 3 2 No 0.00000000000000
Task 4 2,3 Yes 1.00000000000000
I just re-read your question and realized that you intend to run this query within a Stored Procedure. If that's the case, you could use techniques from this method and adapt them in a solution that uses a cursor. I hate them, but I doubt it would work any worse than this solution, and wouldn't have the recursion limitation. You'd just store the results into a temp table or table variable and then return the result of the stored procedure from that.

how to get daily profit from sql table

I'm stucking for a solution at the problem of finding daily profits from db (ms access) table. The difference wrt other tips I found online is that I don't have in the table a field "Price" and one "Cost", but a field "Type" which distinguish if it is a revenue "S" or a cost "C"
this is the table "Record"
| Date | Price | Quantity | Type |
-----------------------------------
|01/02 | 20 | 2 | C |
|01/02 | 10 | 1 | S |
|01/02 | 3 | 10 | S |
|01/02 | 5 | 2 | C |
|03/04 | 12 | 3 | C |
|03/03 | 200 | 1 | S |
|03/03 | 120 | 2 | C |
So far I tried different solutions like:
SELECT
(SELECT SUM (RS.Price* RS.Quantity)
FROM Record RS WHERE RS.Type='S' GROUP BY RS.Data
) as totalSales,
(SELECT SUM (RC.Price*RC.Quantity)
FROM Record RC WHERE RC.Type='C' GROUP BY RC.Date
) as totalLosses,
ROUND(totalSales-totaleLosses,2) as NetTotal,
R.Date
FROM RECORD R";
in my mind it could work but obviously it doesn't
and
SELECT RC.Data, ROUND(SUM (RC.Price*RC.QuantitY),2) as DailyLoss
INTO #DailyLosses
FROM Record RC
WHERE RC.Type='C' GROUP BY RC.Date
SELECT RS.Date, ROUND(SUM (RS.Price*RS.Quantity),2) as DailyRevenue
INTO #DailyRevenues
FROM Record RS
WHERE RS.Type='S'GROUP BY RS.Date
SELECT Date, DailyRevenue - DailyLoss as DailyProfit
FROM #DailyLosses dlos, #DailyRevenues drev
WHERE dlos.Date = drev.Date";
My problem beyond the correct syntax is the approach to this kind of problem
You can use grouping and conditional summing. Try this:
SELECT data.Date, data.Income - data.Cost as Profit
FROM (
SELECT Record.Date as Date,
SUM(IIF(Record.Type = 'S', Record.Price * Record.Quantity, 0)) as Income,
SUM(IIF(Record.Type = 'C', Record.Price * Record.Quantity, 0)) as Cost,
FROM Record
GROUP BY Record.Date
) data
In this case you first create a sub-query to get separate fields for Income and Cost, and then your outer query uses subtraction to get actual profit.

SQLlite strftime function to get grouped data by months

i have table with following structure and data:
I would like to get grouped data by months in given date range for example (from 2014-01-01 to 2014-12-31). Data for some months cannot be available but i still need to have in result information that in given month is result 0.
Result should have following format:
MONTH | DIALS_CNT | APPT_CNT | CONVERS_CNT | CANNOT_REACH_CNT |
2014-01 | 100 | 50 | 20 | 30 |
2014-02 | 100 | 40 | 30 | 30 |
2014-03 | 0 | 0 | 0 | 0 |
etc..
WHERE
APPT_CNT = WHERE call.result = APPT
CONVERS_CNT = WHERE call.result = CONV_NO_APPT
CANNOT_REACH_CNT = WHERE call.result = CANNOT_REACH
How can i do it please with usage function strftime ?
Many thanks for any help or example.
SELECT Month,
(SELECT COUNT(*)
FROM MyTable
WHERE date LIKE Month || '%'
) AS Dials_Cnt,
(SELECT SUM(Call_Result = 'APPT')
FROM MyTable
WHERE date LIKE Month || '%'
) AS Appt_Cnt,
...
FROM (SELECT '2014-01' AS Month UNION ALL
SELECT '2014-02' UNION ALL
SELECT '2014-03' UNION ALL
...
SELECT '2014-12')