Not in Condition in the SQL - sql-server

Productid
Dept cd
123
440
123
422
123
248
1234
422
1234
440
1234
196
12
440
12
422
12
19
12
196
12345
196
12345
180
12345
422
** I should get the Product ID who has 422 and 440 but it should not have 248.
Answer should be 1234 and 12

There are a number of approaches to solve this one.
Here's one option that I think is simplest to understand (i.e. might not be the most efficient!):
SELECT product_id
, Sum(CASE WHEN dept_cd = 422 THEN 1 ELSE 0 END) AS has_422
, Sum(CASE WHEN dept_cd = 440 THEN 1 ELSE 0 END) AS has_440
, Sum(CASE WHEN dept_cd = 248 THEN 1 ELSE 0 END) AS has_248
FROM your_table
WHERE dept_cd IN (442, 440, 248)
GROUP
BY product_id
;
Filtering the results of that should then be trivial (make it a CTE or a subquery then add another WHERE clause, or even just use a HAVING clause).

gvee's answer using aggregation is probably the most efficient one. Simplified version (which can be easily extended):
SELECT product_id
FROM your_table
WHERE dept_cd IN (442, 440, 248)
GROUP BY product_id
HAVING -- assuming (product_id,dept_id) is unique
Sum(CASE WHEN dept_cd IN (422, 440) THEN 1 -- both exist -> result = 2
WHEN dep_cd IN (248) THEN -1 -- exists -> result < 2
END) = 2

Related

How to check values of different rows of a table

I have below sample input table. In real it has lots of records.
Input:
ID
Classification
123
1
123
2
123
3
123
4
657
1
657
3
657
4
For a 'ID', I want it's records should have 'Classification' column contains all the values 1, 2, 3 and 4. If any of these values are not present then that ID's records should be considered as an exception. The output should be as below.
ID
Classification
Flag
123
1
0
123
2
0
123
3
0
123
4
0
657
1
1
657
3
1
657
4
1
Can someone please help me with how can this can be achieved in sql server.
Thanks.
There are a couple of options here, which is more performant is up to you to test, not me (especially when I don't know what indexes you have). One uses conditional aggregation, to check that all the values are there, and the other uses a subquery and counts the DISTINCT values (as I don't know if there could be duplicate classifications):
SELECT *
INTO dbo.YourTable
FROM (VALUES(123,1),
(123,2),
(123,3),
(123,4),
(657,1),
(657,3),
(657,4))V(ID,Classification);
GO
CREATE CLUSTERED INDEX CI_YourIndex ON dbo.YourTable (ID,Classification);
GO
SELECT ID,
Classification,
CASE WHEN COUNT(CASE YT.Classification WHEN 1 THEN 1 END) OVER (PARTITION BY ID) > 0
AND COUNT(CASE YT.Classification WHEN 2 THEN 1 END) OVER (PARTITION BY ID) > 0
AND COUNT(CASE YT.Classification WHEN 3 THEN 1 END) OVER (PARTITION BY ID) > 0
AND COUNT(CASE YT.Classification WHEN 4 THEN 1 END) OVER (PARTITION BY ID) > 0 THEN 1 ELSE 0
END AS Flag
FROM dbo.YourTable YT;
GO
SELECT ID,
Classification,
CASE (SELECT COUNT(DISTINCT sq.Classification)
FROM dbo.YourTable sq
WHERE sq.ID = YT.ID
AND sq.Classification IN (1,2,3,4)) WHEN 4 THEN 1 ELSE 0
END AS Flag
FROM dbo.YourTable YT;
GO
DROP TABLE dbo.YourTable;

How to show order fulfilment in a SQL Server 2008 query

I am trying to think of a way on a SQL Server 2008 database to run through a sales order table and get open demand for a part, order it by due date, then look at a purchase order table and fulfill the sales orders by PO, ordering the PO supply by due date as well. At the same time, I need to show what PO(s) are fulfilling the sales order.
For example:
SO table
SO# DueDate Part Number Required QTY
---------------------------------------------
100 9/3/16 1012 2
101 9/12/16 1012 1
107 10/11/16 1012 4
103 10/17/16 1012 7
PO table:
PO# DueDate Part Number Ordered QTY
--------------------------------------------
331 9/1/16 1012 1
362 9/2/16 1012 1
359 9/24/16 1012 5
371 10/1/16 1012 3
380 10/10/16 1012 10
With this data, I would like to see this result:
SO# DueDate Part Number Required QTY PO number QTY Used QTY Remain
--------------------------------------------------------------------------
100 9/3/16 1012 2 331 1 0
100 9/3/16 1012 1 362 1 0
101 9/12/16 1012 1 359 1 4
107 10/11/16 1012 4 359 4 0
103 10/17/16 1012 7 371 3 0
103 10/17/16 1012 7 380 4 6
I have done this sales order fulfillment process before, but not to the point of breaking down what PO(s) are fulfilling the order, only to the point of summing all open supply, then running through and subtracting the supply from each sales order to get a running balance of supply left.
Many thanks in advance for your help.
I found a bit weird solution, hope it helps you. Maybe later I could optimize it, but now I post it as is:
;WITH cte AS (
SELECT 1 as l
UNION ALL
SELECT l+1
FROM cte
WHERE l <= 1000000
), SO_cte AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate ASC) as rn
FROM SO s
CROSS JOIN cte c
WHERE c.l <= s.[Required QTY]
), PO_cte AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate ASC) as rn
FROM PO p
CROSS JOIN cte c
WHERE c.l <= p.[Ordered QTY]
), almost_done AS (
SELECT DISTINCT
s.SO#,
s.DueDate,
s.[Part Number],
p.PO#,
s.[Required QTY],
p.[Ordered QTY]
FROM SO_cte s
LEFT JOIN PO_cte p
ON p.rn = s.rn
), final AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate) AS RN
FROM almost_done
)
SELECT f.SO#,
f.DueDate,
f.[Part Number],
f.[Required QTY],
f.PO#,
CASE WHEN f.[Ordered QTY]>f.[Required QTY]
THEN ISNULL(ABS(f1.[Required QTY]-f1.[Ordered QTY]),f.[Required QTY])
ELSE f.[Ordered QTY] END
as [QTY Used],
f.[Ordered QTY] -
CASE WHEN f1.PO# = f.PO#
THEN f1.[Ordered QTY]
ELSE
CASE WHEN f.[Ordered QTY]>f.[Required QTY]
THEN ISNULL(ABS(f1.[Required QTY]-f1.[Ordered QTY]),f.[Required QTY])
ELSE f.[Ordered QTY] END
END as [QTY Remain]
FROM final f
LEFT JOIN final f1
ON f.RN = f1.RN+ 1
AND (f.SO# = f1.SO# OR f.PO# = f1.PO#)
OPTION(MAXRECURSION 0)
Output for data you provided:
SO# DueDate Part Number Required QTY PO# QTY Used QTY Remain
100 2016-09-03 1012 2 331 1 0
100 2016-09-03 1012 2 362 1 0
101 2016-09-12 1012 1 359 1 4
107 2016-10-11 1012 4 359 4 0
103 2016-10-17 1012 7 371 3 0
103 2016-10-17 1012 7 380 4 6

Group by and sum based on column values without sum() over()?

We have a table [Kpis] that looks like the following:
RawId EmpId Date Hour Min KpiValue KpiName
106 ABC123 20160310 8 0 3 Kpi1
124 ABC123 20160310 8 0 65 Kpi1
121 ABC123 20160310 8 15 12 Kpi2
109 ABC109 20160310 8 0 34 Kpi2
112 ABC908 20160310 9 5 3 Kpi1
118 ABC907 20160310 8 30 24 Kpi1
115 ABC123 20160310 8 15 54 Kpi1
I would like to group by EmpId, KpiName, Date, Hour. So, for example, with this data, Kpi1 for EmpId ABC123 at Hour 8 would be 122.
So I tried using the CASE statement, but the result is incorrect. I haven't checked the actual totals in the result, but the sums should be correct. It's the format of the result that's incorrect; every empid has two rows: one for Kpi1 and one for Kpi2.
select empid,
case kpiname when 'Kpi1' then sum(kpivalue) end as 'Kpi1',
case kpiname when 'Kpi2' then sum(kpivalue) end as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour
How can I use the Case statement to fix the results?
Thanks.
Put the case inside your sum, such that you for each KpiName only sums the relevant values.
SELECT
EmpId,
[Hour],
SUM(
CASE
WHEN KpiName = 'Kpi1' THEN KpiValue
ELSE 0
END
) Kpi1,
SUM(
CASE
WHEN KpiName = 'Kpi2' THEN KpiValue
ELSE 0
END
) Kpi2
FROM
Kpis
GROUP BY
EmpId,
[Hour]
This produces this output
EmpId Hour Kpi1 Kpi2
ABC109 8 0 34
ABC123 8 122 12
ABC907 8 24 0
ABC908 9 3 0
SUM fucntion have to be outside of CASE:
select empid,
sum(case kpiname when 'Kpi1' then kpivalue end) as 'Kpi1',
sum(case kpiname when 'Kpi2' then kpivalue end) as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour
You can also do this with the PIVOT functionality, which I believe is what you're actually trying to accomplish.
SELECT
*
FROM (
SELECT
EmpId,
KpiName,
[Hour],
KpiValue
FROM
Kpis
) SourceTable
PIVOT (
SUM(KpiValue)
FOR KpiName
IN ([Kpi1],[Kpi2])
) PivotTable
Which gives this output. Note the NULLs as opposed to the zeros, correctly showing the lack of data.
EmpId Hour Kpi1 Kpi2
ABC109 8 NULL 34
ABC123 8 122 12
ABC907 8 24 NULL
ABC908 9 3 NULL

Getting records from a table which have non-null values for a particular column in all months

I have a table:
Project_Id Period Value
123 Jan-15 0
123 Feb-15 34
123 Mar-15 78
123 Apr-15 56
456 Jan-15 0
456 Feb-15 0
456 Mar-15 0
456 Apr-15 0
789 Jan-15 45
789 Feb-15 4
789 Mar-15 18
789 Apr-15 26
I need to retrieve Project data only when i do not have 0 for Value field in all the months like:
Project_Id Period Value
123 Jan-15 0
123 Feb-15 34
123 Mar-15 78
123 Apr-15 56
789 Jan-15 45
789 Feb-15 4
789 Mar-15 18
789 Apr-15 26
Project no 456 should not come in my result because for all the months the value is 0 for that particular project.
Can someone help me with the query?
Use SUM and COUNT to determine the number of 0 Values:
SELECT *
FROM tbl
WHERE project_id IN(
SELECT project_id
FROM tbl
GROUP BY project_id
HAVING SUM(CASE WHEN Value = 0 THEN 1 ELSE 0 END) <> COUNT(*)
)
SQL Fiddle
Another solution is to use EXISTS:
SELECT *
FROM tbl t1
WHERE EXISTS(
SELECT 1 FROM tbl t2 WHERE t2.project_id = t1.project_id AND t2.Value > 0
)
SQL Fiddle
The inner select gets all project_ids that have a least one value that is not 0.
select * from your_table
where project_id in
(
select project_id
from your_table
group by project_id
having sum(case when value <> 0 then 1 else 0 end) > 0
)
Some test data but idea remains the same
create table #test123
(
pid int,
value int
)
insert into #test123
select 1,0
union all
select 1,1
union all
select 2,0
union all
select 2,0
union all
select 3,2
select * from #test123 t2 where exists (select 1 from #test123 t1
where t1.pid=t2.pid
group by pid
having sum(value)>0
)
For performance, I prefer not making a join to check for repeating values:
;WITH CTE as
(
SELECT
Project_Id,
Period,
Value,
max(abs(value)) over (Partition by Period) value
FROM YourTable
)
SELECT
Project_Id,
Period,
Value
FROM CTE
WHERE value > 0
*using abs to check for negative values. If all values are positive, the abs can be omitted.

How to update older "duplicate" records (duplicate except for the date column)

We have a table that contains, for this example, links to demographic questions (questionID) for each subscriber, with a date indicating when the subscriber answered a particular demographic question. In some cases, a subscriber may have answered the same question again at a later date, and we now have multiple records for the same subscriber and questionID, but with different answer dates (see sample data):
subscriberID questionID dateAnswered isDeleted
------------ ----------- ----------------------- ---------
100 559 2015-07-29 13:07:26.153 0
100 560 2015-07-29 13:07:26.153 0
100 561 2015-07-29 13:07:26.153 0
100 562 2015-07-29 13:07:26.153 0
100 575 2015-07-29 13:07:26.153 0
102 559 2015-07-30 15:12:46.143 0
102 564 2015-07-30 15:12:46.143 0
102 588 2015-07-30 15:12:46.143 0
102 559 2015-07-31 16:11:53.323 0
114 575 2015-08-21 11:27:14.253 0
114 588 2015-08-21 11:27:14.253 0
114 560 2015-08-21 11:27:14.253 0
114 588 2015-08-24 05:44:42.030 0
114 562 2015-08-21 11:27:14.253 0
114 575 2015-08-24 05:44:42.030 0
The app that was storing the answers should have flagged the older records as "deleted" (set isDeleted = 1) but it did not do so, and I now need to clean up the older records.
This seems like it should be simple, but it's got me stumped. How do I (a) select any records where there are duplicate subscriberID and questionIDs but with different answer dates? And (b) how do I do an update to set all but the newest records for each subscriber to have isDeleted=1?
Any help would be appreciated! I suspect a self join may be in order, but I haven't figured it out yet. Thus the question!
;WITH X AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY
subscriberID, questionID
ORDER BY dateAnswered DESC) rn
, *
FROM TableName
)
UPDATE X
SET isDeleted = 1
WHERE rn > 1
The select /update below will affect all records that are not marked as deleted , except for the last record by each subscriber for each question. Just another approach.
WITH LastAnswers AS
(
SELECT subscriberID ,questionID , MAX(dateAnswered) AS LastAnsweredDate
FROM TableName
GROUP BY subscriberID ,questionID
)
UPDATE TableName
SET TableName.isDeleted = 1
FROM
TableName
LEFT JOIN LastAnswers
ON TableName.subscriberID = LastAnswers.subscriberID
AND TableName.questionID = LastAnswers.questionID
AND TableName.dateAnswered = LastAnswers.LastAnsweredDate
WHERE LastAnswers.LastAnsweredDate IS NULL AND TableName.isDeleted = 0

Resources