How to add a count/sum and group by in a CTE - sql

Just a question on displaying a row on flight level and displaying a count on how many crew members on that flight.
I want to change the output so it will only display a single record at flight level and it will display two additional columns. One column (cabincrew) is the count of crew members that have the 'CREWTYPE' = 'F' and the other column (cockpitcrew) is the count of crew members that have the `'CREWTYPE' = 'C'.
So the query result should look like:
Flight DepartureDate DepartureAirport CREWBASE CockpitCrew CabinCrew
LS361 2016-05-19 BFS BFS 0 3
Can I have a little help tweaking the below query please:
WITH CTE AS (
SELECT cd.*, c.*, l.Carrier, l.FlightNumber, l.Suffix, l.ScheduledDepartureDate, l.ScheduledDepartureAirport
FROM
(SELECT *, ROW_NUMBER() OVER(PARTITION BY LegKey ORDER BY UpdateID DESC) AS RowNumber FROM Data.Crew) c
INNER JOIN
Data.CrewDetail cd
ON c.UpdateID = cd.CrewUpdateID
AND cd.IsPassive = 0
AND RowNumber = 1
INNER JOIN
Data.Leg l
ON c.LegKey = l.LegKey
)
SELECT
sac.Airline + CAST(sac.FlightNumber AS VARCHAR) + sac.Suffix AS Flight
, sac.DepartureDate
, sac.DepartureAirport
, sac.CREWBASE
, sac.CREWTYPE
, sac.EMPNO
, sac.FIRSTNAME
, sac.LASTNAME
, sac.SEX
FROM
Staging.SabreAssignedCrew sac
LEFT JOIN CTE cte
ON sac.Airline + CAST(sac.FlightNumber AS VARCHAR) + sac.Suffix = cte.Carrier + CAST(cte.FlightNumber AS VARCHAR) + cte.Suffix
AND sac.DepartureDate = cte.ScheduledDepartureDate

PLEASE TRY THIS.
SELECT Flight,
DepartureDate,
DepartureAirport,
CREWBASE,
SUM(CASE WHEN CREWTYPE = 'F' THEN 1 ELSE 0 END) AS CabinCrew ,
SUM(CASE WHEN CREWTYPE = 'C' THEN 1 ELSE 0 END) AS CockpitCrew
FROM #Table
GROUP BY Flight, DepartureDate, DepartureAirport, CREWBASE

Please Try This:
select Flight, DepartureDate, DepartureAirport,CREWBASE,
count(case when CREWTYPE='F' then 1 end ) as CabinCrew,count(case when CREWTYPE='C' then 1 end ) as CockpitCrew
from Staging.SabreAssignedCrew
group by Flight, DepartureDate, DepartureAirport,CREWBASE

Related

SQL: Changing SELECT Query to Show Different Values on Column A based off Boolean in Column B

I have a SQL Query that produces this output:
How would I adjust my query so that the "SCORE" column's value is increased by 3 if the "CROSSDOMAIN" column value is true? I am thinking that I would wrap my existing query with another query that handles this case, but I'm not sure what that query would be.
My existing query is as follows (not the prettiest, but it works):
SELECT HORSUM.TEAMNAME, HORSUM.EMAIL1, SUM(HORSUM.TOTALSUM) Score, COUNT(*) Count, TEAM.THEME, TEAM.FINALIST, TEAM.WINNER, TEAM.CROSSDOMAIN
FROM
(SELECT "JUDGEEMAIL", "TEAMNAME", "EMAIL1", "DEVOPSPRACTICES" + "BUSINESSIMPACT" +
"NEWSKILLS" + "EXECUTION"
+ "INNOVATION" + "CLARITY" as totalSum
FROM "DOMAINSCORES") AS HORSUM
INNER JOIN (SELECT "THEME", "TEAMNAME" as nameID, "FINALIST", "WINNER", "CROSSDOMAIN" FROM "TEAMS" WHERE "FINALIST" = True) AS TEAM ON HORSUM.TEAMNAME = TEAM.nameID
GROUP BY "TEAMNAME", EMAIL1, THEME, FINALIST, WINNER, CROSSDOMAIN
ORDER BY Score DESC;
EDIT: I've tried this query, as suggested by Michael, but I get a syntax error. I also want to add that I am using IBM's DB2, if that makes a difference.
SELECT HORSUM.TEAMNAME
, HORSUM.EMAIL1
, case
when TEAM.CROSSDOMAIN = 'true'
then SUM(HORSUM.TOTALSUM) + 3
else SUM(HORSUM.TOTALSUM)
end as Score
, COUNT(*) Count
, TEAM.THEME
, TEAM.FINALIST
, TEAM.WINNER
, TEAM.CROSSDOMAIN
FROM
(SELECT HORSUM.TEAMNAME, HORSUM.EMAIL1, SUM(HORSUM.TOTALSUM) Score, COUNT(*) Count, TEAM.THEME, TEAM.FINALIST, TEAM.WINNER, TEAM.CROSSDOMAIN
FROM
(SELECT "JUDGEEMAIL", "TEAMNAME", "EMAIL1", "DEVOPSPRACTICES" + "BUSINESSIMPACT" +
"NEWSKILLS" + "EXECUTION"
+ "INNOVATION" + "CLARITY" as totalSum
FROM "DOMAINSCORES") AS HORSUM
INNER JOIN (SELECT "THEME", "TEAMNAME" as nameID, "FINALIST", "WINNER", "CROSSDOMAIN" FROM "TEAMS" WHERE "FINALIST" = True) AS TEAM ON HORSUM.TEAMNAME = TEAM.nameID
GROUP BY "TEAMNAME", EMAIL1, THEME, FINALIST, WINNER, CROSSDOMAIN)
ORDER BY Score DESC;
Try this as outer SELECT
SELECT HORSUM.TEAMNAME
, HORSUM.EMAIL1
, case
when TEAM.CROSSDOMAIN = 'true'
then SUM(HORSUM.TOTALSUM) + 3
else SUM(HORSUM.TOTALSUM)
end as Score
, COUNT(*) Count
, TEAM.THEME
, TEAM.FINALIST
, TEAM.WINNER
, TEAM.CROSSDOMAIN
...

How to get data from 2 rows which has same data in all columns except one in MSSQL

As in my title I want to take data from 2 rows but In my case each 2nd row has one different value compare to the first row.
I want to take all the common data along with the different data as a single row .
Here you can see each row has same values in another row except the 2nd rows last column.
Thanks.
Edits Result :
I suspect you have a some kind of ordering columns that could specify your actual data ordering if so, then you can use row_number() function
select * from (
select *,
row_number() over (partition by <common data cols> order by ? desc) Seq
from table t
) t
where seq = 1;
EDIT : I don't believe your inventort_item_id columns but yes you could use creation_date for ordering purpose
SELECT
EPI.ITEM_CODE, LMP.PROD_DESC, LLPC.COLOC_PROD_PRICE,
BASE_PATH + '' + EPI.IMAGE_FOLDER_NAME + '/' + EPI.IMAGE_DESCRIPTION AS POPULAR_PRODUCTS_IMAGE_PATHS
FROM (SELECT *,
ROW_NUMBER() OVER (PARTITION BY ITEM_CODE ORDER BY creation_date DESC) as Seq
FROM ECOM_PRODUCT_IMAGES EPI
) EPI
INNER JOIN ECOM_POPULAR_PRODUCTS_MAPPING EPPIM ON EPPIM.ITEM_CODE = EPI.ITEM_CODE
INNER JOIN LOM_MST_PRODUCT LMP ON LMP.PROD_CODE = EPI.ITEM_CODE
INNER JOIN LOM_LNK_PROD_COMP LLPC ON LLPC.COLOC_PROD_CODE = LMP.PROD_CODE
WHERE EPI.Seq = 1 AND
EPPIM.ITEM_STATUS = 'ACTIVE';
EDIT 2: In that case you need to use GROUP BY clause with conditional aggregation
SELECT
EPI.ITEM_CODE, LMP.PROD_DESC, LLPC.COLOC_PROD_PRICE,
MAX(CASE WHEN EPI.Seq = 2
THEN (BASE_PATH + '' + EPI.IMAGE_FOLDER_NAME + '/' + EPI.IMAGE_DESCRIPTION)
END) AS POPULAR_PRODUCTS_IMAGE_PATHS,
MAX(CASE WHEN EPI.Seq = 1
THEN (BASE_PATH + '' + EPI.IMAGE_FOLDER_NAME + '/' + EPI.IMAGE_DESCRIPTION)
END) AS PATH_NEW
FROM (SELECT *,
ROW_NUMBER() OVER (PARTITION BY ITEM_CODE ORDER BY creation_date DESC) as Seq
FROM ECOM_PRODUCT_IMAGES EPI
) EPI
INNER JOIN ECOM_POPULAR_PRODUCTS_MAPPING EPPIM ON EPPIM.ITEM_CODE = EPI.ITEM_CODE
INNER JOIN LOM_MST_PRODUCT LMP ON LMP.PROD_CODE = EPI.ITEM_CODE
INNER JOIN LOM_LNK_PROD_COMP LLPC ON LLPC.COLOC_PROD_CODE = LMP.PROD_CODE
WHERE EPPIM.ITEM_STATUS = 'ACTIVE'
GROUP BY EPI.ITEM_CODE, LMP.PROD_DESC, LLPC.COLOC_PROD_PRICE;
here is my approach, also using a window function.
sample data
if object_id('tempdb..#x') is not null drop table #x
CREATE TABLE #x (ITEM_CODE VARCHAR(10), PROD_DESC VARCHAR(20),
COLOR_PROD_PRICE DECIMAL, POPULAR_PRODUCTS_IMAGE_PATHS VARCHAR(200))
INSERT INTO #X(ITEM_CODE,PROD_DESC,COLOR_PROD_PRICE,POPULAR_PRODUCTS_IMAGE_PATHS) VALUES
('P0001', 'Axe Brand', 88.000, 'some_path_to_img1.jpg'),
('P0001', 'Axe Brand', 88.000, 'some_path_to_img2.jpg'),
('P0002', 'Almond Nuts', 499.000, 'some_path_to_img1.jpg'),
('P0002', 'Almond Nuts', 499.000, 'some_path_to_img2.jpg')
query - just change #x to your table and it should work
;WITH my_cte as
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY ITEM_CODE ORDER BY POPULAR_PRODUCTS_IMAGE_PATHS) AS 'track_row'
FROM #x
)
SELECT a.ITEM_CODE, a.PROD_DESC, a.COLOR_PROD_PRICE,
a.POPULAR_PRODUCTS_IMAGE_PATHS + ' ' + b.POPULAR_PRODUCTS_IMAGE_PATHS AS 'POPULAR_PRODUCTS_IMAGE_PATHS'
FROM my_cte AS a
INNER JOIN
my_cte AS b ON a.ITEM_CODE=b.ITEM_CODE
WHERE a.track_row=1 AND b.track_row=2
output
ITEM_CODE PROD_DESC COLOR_PROD_PRICE POPULAR_PRODUCTS_IMAGE_PATHS
P0001 Axe Brand 88 some_path_to_img1.jpg some_path_to_img2.jpg
P0002 Almond Nuts 499 some_path_to_img1.jpg some_path_to_img2.jpg

How to get alternate row using SQL query?

I have a requirement which I need to produce the result that returns alternately 1 and 0.
SELECT *
FROM
(SELECT
id
,itemNo
,convert(int,tStationsType_id) as tStationsType_id
,tSpecSetpoint_descriptions_id
,SetpointValue
,rowEvenOdd
FROM
TEST S
INNER JOIN
(SELECT
itemNo AS IM, tStationsType_id as ST,
ROW_NUMBER() OVER(PARTITION BY itemNo ORDER BY itemNo) % 2 AS rowEvenOdd
FROM TEST
GROUP BY itemNo, tStationsType_id) A ON S.itemNo = A.IM
AND S.tStationsType_id = A.ST) t
WHERE
itemno = '1000911752202'
ORDER BY
tStationsType_id
The result I get is something like below.
I would like to produce alternate 1 and 0 in rowEvenOdd. However I notice it I can't get it alternate if I order by tStationsType_id.
So basically, what I want is when the
StationsType_id = 2, then rowEvenOdd = 0
StationsType_id = 3, then rowEvenOdd = 1
StationsType_id = 6, then rowEvenOdd = 0
StationsType_id = 8, then rowEvenOdd = 1
StationsType_id = 10, then rowEvenOdd = 0
Can someone help me with this query?
Thanks.
If you just need alternating 0 and 1 in the result set, use SEQUENCE like this:
CREATE SEQUENCE EvenOdd
START WITH 0
INCREMENT BY 1
MAXVALUE 1
MINVALUE 0
CYCLE;
GO
SELECT SalesId, NEXT VALUE FOR EvenOdd as EvenOddColumn FROM Sales
DROP SEQUENCE EvenOdd
To learn more, go to the MSDN page on sequences here: https://msdn.microsoft.com/en-us/library/ff878091.aspx
you can Use case when then
SELECT
case
when exists (SELECT 1 FROM StationsTypeMaster
M WHERE M.StationsType_id=A.StationsType_id)
then 1
else 0 end as rowEvenOdd
,*
FROM
(
select id
,itemNo
,convert(int,tStationsType_id) as tStationsType_id
,tSpecSetpoint_descriptions_id
,SetpointValue
,rowEvenOdd from TEST S
inner join
(select itemNo AS IM,tStationsType_id as ST,
ROW_NUMBER() OVER(PARTITION BY itemNo ORDER BY itemNo)%2 AS rowEvenOdd
from TEST
group by itemNo,tStationsType_id
)A
on S.itemNo = A.IM
and S.tStationsType_id = A.ST) t
where itemno = '1000911752202'
order by tStationsType_id
Use DENSE_RANK instead of ROW_NUMBER:
SELECT * FROM
(
SELECT
id,
itemNo
convert(int,tStationsType_id) as tStationsType_id,
tSpecSetpoint_descriptions_id,
SetpointValue,
(DENSE_RANK() OVER(ORDER BY CAST(A.ST as INT)) - 1)%2 AS rowEvenOdd
FROM
TEST S
JOIN
(
SELECT
itemNo AS IM,
tStationsType_id as ST
FROM
TEST
GROUP BY
itemNo,tStationsType_id
)A
ON
S.itemNo = A.IM
and S.tStationsType_id = A.ST
) t
WHERE
itemno = '1000911752202'
ORDER BY
tStationsType_id

Filter rows by count of two column values

I have following table:
Card(
MembershipNumber,
EmbossLine,
status,
EmbossName
)
with sample data
(0009,0321,'E0','Finn')
(0009,0322,'E1','Finn')
(0004,0356,'E0','Mary')
(0004,0398,'E0','Mary')
(0004,0382,'E1','Mary')
I want to retrieve rows such that only those rows should appear that have count of MembershipNumber > 1 AND count of status='E0' > 1.
For Example The query should return following result
(0004,0356,'E0','Mary')
(0004,0398,'E0','Mary')
I have the query for filtering it with MembershipNumber count but cant figure out how to filter by status='E0'. Here's the query so far
SELECT *
FROM (SELECT *,
Count(MembershipNumber)OVER(partition BY EmbossName) AS cnt
FROM card) A
WHERE cnt > 1
You can just add WHERE status = 'E0' inside your subquery:
SQL Fiddle (credit to Raging Bull for the fiddle)
SELECT *
FROM (
SELECT *,
COUNT(MembershipNumber) OVER(PARTITION BY EmbossName) AS cnt
FROM card
WHERE status = 'E0'
)A
WHERE cnt > 1
You can do it this way:
select t1.*
from card t1 left join
(select EmbossName
from card
where [status]='E0'
group by EmbossName,[status]
having count(MembershipNumber)>1 ) t2 on t1.EmbossName=t2.EmbossName
where t2.EmbossName is not null and [status]='E0'
Result:
MembershipNumber EmbossLine status EmbossName
---------------------------------------------------
4 356 E0 Mary
4 398 E0 Mary
Sample result in SQL Fiddle
try :
WITH cnt AS (
SELECT MembershipNumber, status
FROM Card
WHERE status = 'E0'
GROUP BY MembershipNumber, status
HAVING COUNT(MembershipNumber) > 1 AND COUNT(status) > 1
)
SELECT c.*
FROM Card c
INNER JOIN cnt
ON c.MembershipNumber = cnt.MembershipNumber
AND c.status = cnt.status;
You can try this:
DECLARE #DataSource TABLE
(
[MembershipNumber] SMALLINT
,[EmbossLine] SMALLINT
,[status] CHAR(2)
,[EmbossName] VARCHAR(8)
);
INSERT INTO #DataSource ([MembershipNumber], [EmbossLine], [status], [EmbossName])
VALUES (0009,0321,'E0','Finn')
,(0009,0322,'E1','Finn')
,(0004,0356,'E0','Mary')
,(0004,0398,'E0','Mary')
,(0004,0382,'E1','Mary');
SELECT [MembershipNumber]
,[EmbossLine]
,[status]
,[EmbossName]
FROM
(
SELECT *
,COUNT([MembershipNumber]) OVER (PARTITION BY [EmbossName]) AS cnt1
,SUM(IIF([status] = 'E0' , 1, 0)) OVER (PARTITION BY [EmbossName]) AS cnt2
FROM #DataSource
) DS
WHERE cnt1 > 1
AND cnt2 > 1
AND [status] = 'E0';
The idea is to add a second counter, but instead of COUNT function to use SUM function for counting only the rows that have [status] = 'E0'. Then, in the where clause we are filtering by the two counters and [status] = 'E0'.

T-sql problem with running sum

I am trying to write T-sql script which will find "open" records for one table
Structure of data is following
Id (int PK) Ts (datetime) Art_id (int) Amount (float)
1 '2009-01-01' 1 1
2 '2009-01-05' 1 -1
3 '2009-01-10' 1 1
4 '2009-01-11' 1 -1
5 '2009-01-13' 1 1
6 '2009-01-14' 1 1
7 '2009-01-15' 2 1
8 '2009-01-17' 2 -1
9 '2009-01-18' 2 1
According to my needs I am trying to show only records after last sum for every one articles where 0 sorting by date of last running sum of zero value. So I am trying to abstract (show) records 5 and 6 for Art_id=1 and record 9 for art_id=2. I am using MSSQL2005 and my table has around 30K records with 6000 distinct values of ART_ID.
In this solution I simply want to find all the rows where there isn't a subsequent row for that Art_id where the running sum was 0. I am assuming we can use the ID as a better tiebreaker than TS, since two rows can come in with the same timestamp but they will get sequential identity values.
;WITH base AS
(
SELECT
ID, Art_id, TS, Amount,
RunningSum = Amount + COALESCE
(
(
SELECT SUM(Amount)
FROM dbo.foo
WHERE Art_id = f.Art_id
AND ID < f.ID
)
, 0
)
FROM dbo.[table name] AS f
)
SELECT ID, Art_id, TS, Amount
FROM base AS b1
WHERE NOT EXISTS
(
SELECT 1
FROM base AS b2
WHERE Art_id = b1.Art_id
AND ID >= b1.ID
AND RunningSum = 0
)
ORDER BY ID;
Complete working query:
SELECT
*
FROM TABLE_NAME E
JOIN
(SELECT
C.ART_ID,
MAX(TS) MAX_TS
FROM
(SELECT
ART_ID,
TS,
COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
FROM TABLE_NAME A) C
WHERE C.ROW_SUM = 0
GROUP BY C.ART_ID) D
ON
(D.ART_ID = E.ART_ID) AND
(E.TS >= D.MAX_TS)
First we calculate running sums for every row:
SELECT
ART_ID,
TS,
COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
FROM TABLE_NAME A
Then we look for last article with 0:
SELECT
C.ART_ID,
MAX(TS) MAX_TS
FROM
(SELECT
ART_ID,
TS,
COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
FROM TABLE_NAME A) C
WHERE C.ROW_SUM = 0
GROUP BY C.ART_ID
You can find all rows where the running sum is zero with:
select cur.id, cur.art_id
from #articles cur
left join #articles prev
on prev.art_id = cur.art_id
and prev.id <= cur.id
group by cur.id, cur.art_id
having sum(prev.amount) = 0
Then you can query all rows that come after the rows with a zero running sum:
select a.*
from #articles a
left join (
select cur.id, cur.art_id, running = sum(prev.amount)
from #articles cur
left join #articles prev
on prev.art_id = cur.art_id
and prev.ts <= cur.ts
group by cur.id, cur.art_id
having sum(prev.amount) = 0
) later_zero_running on
a.art_id = later_zero_running.art_id
and a.id <= later_zero_running.id
where later_zero_running.id is null
The LEFT JOIN in combination with the WHERE says: there can not be a row after this row, where the running sum is zero.