Selecting Rows That Have One Value but Not Another - sql

I need a get some rows of two tables with join that shoud have one value in a column (1407) but shouldn't have other value (1403)
These is the tables and the query:
select a.job, a.date, b.group from log a inner join active_tmp b
on a.jobno=b.jobno and a.no=b.no where b.list = 'N'
AND LOGDATE = TO_CHAR(TRUNC(SYSDATE),'YYYYMMDD')
and a.job not like 'HOUSE%'
and a.job not like 'CAR%' and (errorCode=1047 and errorCode<>1403);
LOG
JOB DATE LOGDATE JOBNO NO errorCode
MAM 20220123 20220125 33 22 1047
MAM 20220123 20220125 33 22 1403
DAD 20220122 20220125 11 99 1047
MAM 20220122 20220125 33 22 0323
DAD 20220122 20220125 11 99 0444
ACTIVE_TMP
JOB JOBNO NO GROUP LIST
MAM 33 22 LAPTOP N
MAM 33 22 LAPTOP N
DAD 11 99 KEY N
But I get:
MAM,20220123,LAPTOP
DAD,20220122,KEY
I need:
DAD,20220122,KEY
Because MAM have both codes (1047 and 1043).

To rephrase, I think you mean "I want to return matching rows that have error code 1047 but for which the same values of jobno, no, list do not have a corresponding row with error code 1403"
This part is redundant:
AND (errorCode = 1047 AND errorCode <> 1403);
If you are saying errorCode must be 1047, you are also saying it is not equal to 1403.
I think you want to select some rows into some result set, then check that there's not another row that disqualifies one of the selected rows from the final result.
So,
SELECT a.job,
a.date,
b.group
FROM _log a
INNER JOIN _active_tmp b
ON a.jobno = b.jobno
AND a.no = b.no
WHERE b.list = 'N'
AND LOGDATE = TO_CHAR(CURRENT_TIMESTAMP,'YYYYMMDD')
AND a.job NOT LIKE 'HOUSE%'
AND a.job NOT LIKE 'CAR%'
AND a.errorCode = 1047
AND NOT EXISTS (SELECT 1
FROM _log c
INNER JOIN _active_tmp d
ON c.jobno = d.jobno
AND c.no = d.no
WHERE a.job = c.job
AND a.date = c.date
AND b.group = d.group
AND c.errorCode = 1403)
We select the rows that satisfy the join and have error code 1047 then subtract from that set those rows that also satisfy the join but have error code 1403. You could possibly make this more terse using CTE or a temp table, but this works too.
Note I had to change a few things to make it work in my engine (Postgres), so you may have to change a few things back to Oracle.

You need to change the error code logic. Identify what JOB values has 1403 and then exclude those values
select distinct a.job, a.date, b.[group] from LOG a inner join active_tmp b
on a.jobno=b.jobno and a.no=b.no where b.list = 'N'
AND LOGDATE = TO_CHAR(TRUNC(SYSDATE),'YYYYMMDD')
and a.job not like 'HOUSE%'
and a.job not like 'CAR%' and a.job not in (select JOB from log where errorCode in(1403));

Related

Including rows with no values in SQL query output

Running the following query in my SQL SERVER 2016 database:
SELECT
u.idnumber,
gi.idnumber AS code,
gg.finalgrade AS grade
FROM
grades AS gg
INNER JOIN grade_items AS gi ON gg.itemid = gi.id
INNER JOIN users AS u ON gg.userid = u.id
WHERE gi.idnumber IN ('436','434','313','002','135')
AND (u.idnumber = 'JohnBrown-xyz123);
gives me this result:
-------------------------
idnumber code grade
-------------------------
John12558 313 96
John12558 135 83
How can I include a row for ALL the 5 gi.idnumber's, including the rows with no grade?
-------------------------
idnumber code grade
-------------------------
John12558 436
John12558 434
John12558 313 96
John12558 002
John12558 135 83
Database tables:
user
id PK
grade_items
id PK
grade_grades
grade_grades.itemid FK (grade_items)
grade_grades.userid FK (user)
You can use value construct like that :
select tt.idnumber,
t.idnumber AS code,
tt.grade
from (values ('436'), ('434'), ('313'), ('002'), ('135')) t (idnumber)
left join (select
u.idnumber,
gi.idnumber AS code,
gg.finalgrade AS grade
from grades AS gg
inner join grade_items AS gi ON gg.itemid = gi.id
inner join users AS u ON gg.userid = u.id
where u.idnumber = 'JohnBrown-xyz123') tt on left(tt.code, 3) = t.idnumber
If, idnumber has numeric type, then you don't need to use ' '.
You need cross join to see all you want:
select
u.idnumber,
gi.idnumber code,
ISNULL(gg.finalgrade, 0) grade
from users u
cross join grade_items gi
left outer join grades gg on gg.itemid = gi.id and gg.userid = u.id
where gi.idnumber in ('436','434','313','002','135')
and u.idnumber = 'JohnBrown-xyz123'
nulls in finalgrade cannot be replaced with 'N/A' because they are numbers, not strings, I replaced them with zero.
Assuming "no value" is NULL then just add an extra clause to your WHERE:
WHERE (LEFT(gi.idnumber, 3) IN ('436','434','313','002','135')
OR gi.idnumber IS NULL)
Edit: Apaprently, the above does not work, which implies that "no value" does not mean NULL. you could, instead, therefore try:
WHERE (LEFT(gi.idnumber, 3) IN ('436','434','313','002','135','')
If this doesn't work, please provide consumable sample data, which we can test against: Forum Etiquette: How to post data for a T-SQL Question

aggregating nested SQL statements to fewer columns

I am trying to aggregate my data and group it with respect to SKU's and the cluster ID associated with that SKU.
My current output brings back roughly 40,000 rows (5 SKU's * 8,000 Stores) however I want just 35.
My code:
SELECT DISTINCT E.*
FROM ALC_ITEM_SOURCE P
RIGHT JOIN
(
SELECT D.* ,SUM(L.ALLOCATED_QTY) AS TOTAL_ALLOCATED
FROM ALC_ITEM_LOC L
RIGHT JOIN
(
SELECT C.*
FROM STORE S,
(
SELECT A.*, B.LOCATION AS STORE_NUMBER
FROM FDT_MAP_CLUSTER_LOCATION B,
(
SELECT DISTINCT SS.ALLOC_CLUSTER_ID, SS.ALLOC_CLUSTER_NAME, SS.SKU
from fdt_maptool_sas_data ss
WHERE SS.SKU IN (1099866,
1099896,
1000898,
1000960,
1000988
)
AND SS.ORDER_NO IS NOT NULL
AND ALLOC_CLUSTER_NAME NOT LIKE '%DC Cluster%'
GROUP BY SS.ALLOC_CLUSTER_ID, SS.ALLOC_CLUSTER_NAME, SS.WORKSHEET_ID, SS.SKU
)A
WHERE B.CLUSTER_ID = A.ALLOC_CLUSTER_ID
AND B.LOCATION_TYPE = 'S'
)C
WHERE S.STORE = C.STORE_NUMBER
AND S.STORE_CLOSE_DATE IS NULL
AND S.DISTRICT NOT IN (997, 998, 999)
AND S.STORE_OPEN_DATE <= SYSDATE
)D
ON L.ITEM_ID = D.SKU
AND L.LOCATION_ID = D.STORE_NUMBER
GROUP BY D.ALLOC_CLUSTER_ID, D.ALLOC_CLUSTER_NAME, D.SKU, D.STORE_NUMBER
)E
ON P.ITEM_ID = E.SKU
AND P.SOURCE_TYPE <> 4
AND P.RELEASE_DATE > '01-FEB-2018'
My desired result would contain:
SKU Cluster_ID Total_allocated Count(stores)
1000989 1AA STORES 258 200
1000989 2A STORES 78 600
1000989 B STORES 36 500
1000989 C STORES 114 100
1000989 D STORES 144 1222
1000989 E STORES 168 600
1000989 F STORES 60 501
Which is taking a sum of total allocated per store per cluster ID.
As you can see each SKU has a grade (AA-F), I would want to repeat this 5 times since I have 5 SKU's.
Basically I am asking how can I aggregate my data up to look like the above table from the 40,000 rows it is now.
Any help is appreciated!
Just to make your sql nicer and neat, you should avoid constructing joins in 'where' statement.
Also I think you have nothing to do with ALC_ITEM_SOURCE table, since you did not use it practically.
You may try this version, or at least start working on it:
select SS.ALLOC_CLUSTER_ID,SS.ALLOC_CLUSTER_NAME,SS.SKU,SUM (L.ALLOCATED_QTY) as total_allocated,count(b.location) as store_number
FROM fdt_maptool_sas_data ss
inner join
FDT_MAP_CLUSTER_LOCATION b on B.CLUSTER_ID =A.ALLOC_CLUSTER_ID AND B.LOCATION_TYPE = 'S'
inner join store s on S.STORE = b.location AND S.STORE_CLOSE_DATE IS NULL AND S.DISTRICT NOT IN (997, 998, 999) AND S.STORE_OPEN_DATE <= SYSDATE
left outer join ALC_ITEM_LOC L on L.ITEM_ID = ss.SKU AND L.LOCATION_ID = b.location
WHERE SS.SKU IN (1099866,
1099896,
1000898,
1000960,
1000988)
AND SS.ORDER_NO IS NOT NULL
AND ALLOC_CLUSTER_NAME NOT LIKE '%DC Cluster%'

Manipulating SQL table

I have a table, the structure of which I have simplified to the smaller table below.
I want to manipulate the dataset below into the following form:
The new dataset will contain a single record for each case of DC, with a yes/no flag indicating if the NatureOfTumour has changed from DC to IN, and the time taken to change from DC to IN if applicable.
The change from DC to IN will be considered only if location has remained the same i.e. only those records should be considered where NatureOfTumour has changed from DC to IN and the location remained the same. ItemNo is the unique ID.
On a community member's advice I have pasted the table in text below as well, cleaned up as best as I could. The last column "Gen" is empty. ItemNo is the unique ID. Copying the text below to excel and doing a text-to-columns (separated by spaces) should give you the original table in a readable format. Sorry cant think of a better way to paste the table here.
ItemNo DateOfTest NatureOfTumour Location Centre Gen
2345 07/2006 DC P S-224
2345 12/2006 IN P S-224
2342 05/2004 DC Q B-266
3878 06/2006 DC P S-224
3878 05/2005 DC Q S-224
5678 09/2000 IN P S-224
5597 10/2001 DC P B-266
5597 01/1999 IN Q B-266
Try this. The LEAD function looks at the next row based on groups of ItemNo ordered by DateOfTest.
WITH abc AS (
SELECT
ItemNo
,DateOfTest
,NatureOfTumour
,Location
,Centre
,LEAD(NatureOfTumour) OVER (PARTITION BY ItemNo ORDER BY DateOfTest) as FutureNature
,LEAD(Location) OVER (PARTITION BY ItemNo ORDER BY DateOfTest) as FutureLocation
,LEAD(DateOfTest) OVER (PARTITION BY ItemNo ORDER BY DateOfTest) as FutureDateOfTest
FROM test_results
)
SELECT
ItemNo
,DateOfTest
,NatureOfTumour
,CASE
WHEN FutureNature = 'IN'
AND FutureLocation = Location
THEN 'Yes'
ELSE 'NO'
END AS State_Change
,FutureDateOfTest - DateOfTest as Date_Diff
,Location
,Centre
from abc
WHERE NatureOfTumour = 'DC'
You need a self join. Something along these lines:
SELECT
d.ItemNo,
i.DateOfTest - d.DateOfTest AS datediff,
d.Location,
d.Centre,
d.Gen
FROM
(
SELECT
*
FROM demo
WHERE NatureOfTumour = 'DC'
) d
INNER JOIN
(
SELECT
*
FROM demo
WHERE NatureOfTumour = 'IN'
) i ON d.ItemNo = i.ItemNo
AND d.Location = i.Location;
If I understood your question, you could try this:
Let me know .
If you want in output only the rows who changed (GEN='Y'), change LEFT JOIN to INNER JOIN.
SELECT A.ITEMNO, A.DATEOFTEST, A.NATUREOFTUMOUR, A.LOCATION
, CASE WHEN B.NATUREOFTUMOUR='IN' AND A.LOCATION = B.LOCATION THEN 'Y' ELSE 'N' END AS GEN_NEW
, CASE WHEN B.NATUREOFTUMOUR='IN' AND A.LOCATION = B.LOCATION THEN B.DATEOFTEST-A.DATEOFTEST END AS TIME_PASS
FROM TE A
LEFT JOIN TE B ON A.ITEMNO=B.ITEMNO AND B.NATUREOFTUMOUR<>'DC' AND A.DATEOFTEST < B.DATEOFTEST
WHERE A.NATUREOFTUMOUR='DC
OR (I can't understand from your question)
SELECT A.ITEMNO, A.DATEOFTEST, A.NATUREOFTUMOUR, A.LOCATION
, CASE WHEN B.NATUREOFTUMOUR='IN' THEN 'Y' ELSE 'N' END AS GEN_NEW
, CASE WHEN B.NATUREOFTUMOUR='IN' THEN B.DATEOFTEST-A.DATEOFTEST END AS TIME_PASS
FROM TE A
LEFT JOIN TE B ON A.ITEMNO=B.ITEMNO AND B.NATUREOFTUMOUR<>'DC' AND A.DATEOFTEST < B.DATEOFTEST AND A.LOCATION = B.LOCATION
WHERE A.NATUREOFTUMOUR='DC'\\
Output
ITEMNO DATEOFTEST NATUREOFTUMOUR LOCATION GEN_NEW TIME_PASS
1 2345 01.07.2006 DC P Y 153
2 2342 01.06.2006 DC Q N NULL
3 5597 01.10.2001 DC P N NULL
4 3878 01.05.2005 DC Q N NULL
5 3878 01.06.2006 DC P N NULL

SQL AVG statement another table

I'm having trouble with a SQL query. The goal is to see only the certain entries on a specific date (I got this already) which have an average score below 1 in their last 5 home games.
You can see the tables here:
http://dbup2date.uni-bayreuth.de/downloads/bundesliga/Klassendiagramm_Bundesliga.pdf
I have this code so far:
SELECT
A.Spieltag, A.Datum, A.Uhrzeit, B.Name AS Heim
FROM
Spiel AS A
JOIN
Verein AS B ON A.Heim = B.V_ID AND B.Liga = 1
WHERE
Spieltag = 5
HAVING
AVG(SELECT Tore_Heim
FROM Spiel AS A
JOIN Verein AS B
WHEN A.Heim = B.V_ID) < 1
Sorry for my bad English
Thank you
Make sure you group by any =fields that you are not aggregating on when you use HAVING. And you can simplify that HAVING clause since you are already referencing those exact tables in your FROM:
SELECT
A.Spieltag, A.Datum, A.Uhrzeit, B.Name AS Heim
FROM
Spiel AS A
JOIN
Verein AS B ON A.Heim = B.V_ID AND B.Liga = 1
WHERE
Spieltag = 5
GROUP BY A.Spieltag, A.Datum, A.Uhrzeit, B.Name
HAVING
AVG(Tore_Heim) < 1

Is there any effect on Result due to column ordering in Group by clause in SQL server

If i have 20 columns and want to get result based on group by clause. Is there any effect on result if I change the order of columns in SQL query.
My example is as follows :
Select
R.ClientId
,R.FirmName
,R.StrategyID
,R.SecurityType
,SUM(R.QtySent)
,SUM(R.ExecutedQty) AS ExecutedQty
,SUM(R.CrossedExecutedQty) AS CrossedExecutedQty
FROM ClientDetail m inner join ClientMaster c on
m.clordid = c.masterorderId
and m.msg_id = 43
and c.msg_id in (10,11,12,40)--Msg_Id 40 for manual trade
inner join #ResultsDaily R on c.clordid = R.clordid
GROUP BY R.TethysClientId
,R.FirmName
,R.StrategyID
,R.SecurityType
--Query 1
SELECT R.ClientId --A
,R.FirmName --B
,R.StrategyID --C
,R.SecurityType --D
,SUM(R.QtySent)
,SUM(R.ExecutedQty) AS ExecutedQty
,SUM(R.CrossedExecutedQty) AS CrossedExecutedQty
FROM ClientDetail m
JOIN ClientMaster c
ON m.clordid = c.masterorderId
AND m.msg_id = 43
AND c.msg_id in (10,11,12,40)
JOIN #ResultsDaily R on c.clordid = R.clordid
GROUP BY R.TethysClientId --1
,R.FirmName --2
,R.StrategyID --3
,R.SecurityType --4
In the above Query, 1,2,3 and 4 can be of any order. same time A,B,C and D can be also be in any order.
No column should be missed , thats all.
Parden, if i misinterpreted the Question.