SQL: query one column in same table - sql

A football manager here. How do I:
Select all matches that have kicked-off but never had a goal.
Select all matches that kicked-off more than 1h ago but haven't yet had a goal or a corner-kick.
| Match | Event | EventTime |
|-------------------------------------------|
| 1 | Kick-off | 2014-12-15T16:00:00 |
| 1 | Throw-in | 2014-12-15T16:15:00 |
| 1 | Goal | 2014-12-15T16:20:00 |
| 1 | Corner-kick | 2014-12-15T16:30:00 |
| 1 | End | 2014-12-15T17:30:00 |
| 2 | Kick-off | 2014-12-10T16:00:00 |
| 2 | Goal | 2014-12-10T16:01:00 |
| 3 | Kick-off | 2014-12-05T08:00:00 |
| 3 | Corner-kick | 2014-12-05T08:10:00 |
I feel this should be simple, but I'm stuck somehow.

1:
SELECT DISTINCT Match
FROM dbo.YourTable A
WHERE [Event] = 'Kick-off'
AND NOT EXISTS( SELECT 1 FROM dbo.YourTable
WHERE Match = A.Match
AND [Event] = 'Goal')
2:
SELECT DISTINCT Match
FROM dbo.YourTable A
WHERE [Event] = 'Kick-off'
AND EventTime <= GETDATE(HOUR,-1,GETDATE())
AND NOT EXISTS( SELECT 1 FROM dbo.YourTable
WHERE Match = A.Match
AND [Event] IN ('Goal','Corner-kick'))

You would do this with aggregation and a having clause. For the first:
select match
from table t
group by match
having sum(case when event = 'Kick-off' then 1 else 0 end) > 0 and
sum(case when event = 'Goal' then 1 else 0 end) = 0;
For the second:
select match
from table t
group by match
having max(sum case when event = 'Kick-off' then eventtime end) <= getdate() - 1.0/24 and
sum(case when event in ('Goal', 'Corner-Kick') then 1 else 0 end) = 0;
Each condition in the having clause counts the number of rows that match the condition. > 0 means that at least one row matched. = 0 means no rows match.

Related

Update specific column with the value of single column from another table

I want to update the specific column with value of a single column from another table using sql query.
The specific column I want to update is in the WorkingTime table. Like for example I want to update its value by 1 depending on its IDNo and Date from TABLE 2
WorkingTime table:
IDNo | PeriodDate | SPLP | NVLP | NBLP | PLP | BLP | MLP | SLP | VLP | NSLP |
18-031 |06/11/2017 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
18-032 |06/12/2017 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
18-033 |06/13/2017 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
And the name of the column I want to update in WorkingTime is in the LeaveDetails. The column with the name LeaveType.
LeaveDetails:
Contro | IDNo | LeaveType| DateFrom | DateTo | NoOfDays |
000041 |18-031 | SPLP |06/11/2019|06/11/2019| 1 |
000042 |18-032 | NVLP |06/12/2019|06/12/2019| 1 |
000043 |18-033 | PLP |06/13/2019|06/13/2019| 1 |
And my expected result after the query is this
Expected result:
IDNo | PeriodDate | SPLP | NVLP | NBLP | PLP | BLP | MLP | SLP | VLP | NSLP |
18-031 |06/11/2017 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
18-032 |06/12/2017 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
18-018 |06/13/2017 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
Can anyone please help me with the query i will use to make this happen. Thank you in advance.
I'm unclear which tables/columns you are trying to update but you can update a column value in one table using the column value of a different table by using a sub query, for example:
UPDATE TABLE_1
SET LEAVETYPE =
(SELECT NEW_COLUMN FROM TABLE_2 WHERE PRIMARY_KEY = 'Something')
WHERE PRIMARY_KEY = 'Something'
;
Try to Use temp table. first take your data in temp table then update main table by using temp table data.
Or using join the table
update u
set u.assid = s.assid
from ud u
inner join sale s on
u.id = s.udid
One method is a series of case expressions and subqueries:
update table1
set splp = (case when exists (select 1
from table2 t2
where t2.idno = table1.idno and
t2.perioddate = t1.perioddate and
t2.leavetype = 'SPLP'
)
then 1 else table1.splp
end),
splp = (case when exists (select 1
from table2 t2
where t2.idno = table1.idno and
t2.perioddate = t1.perioddate and
t2.leavetype = 'NVLP'
)
then 1 else table1.nvlp
end),
. . .;
Depending on the database, there are other approach as well, but the above should work in any database.

How to Perform Window Partitioning with Conditions (Oracle)

I need to know how to narrow down my partitioning window with a condition. For instance, if I have the following:
SELECT
T1.*,
COUNT(T1.ID) OVER (PARTITION BY ID)
FROM TBL T1
WHERE /* some other conditions */;
I need the ID partition to only be a subset of all the identical ID's that have T1.TYPE = 'J'.
+---------+--------+---------------+--------+
| ID | TYPE | OTH1 | OTH2 |
+---------+--------+---------------+--------+
| 1 | K | 500 | RER |
| 1 | J | 503 | LEL |
| 1 | J | 534 | KEL |
| 2 | J | 536 | NULL |
| 2 | J | 667 | ERT |
| 2 | J | 98 | NULL |
+---------+--------+---------------+--------+
In this data set, I need to only consider my window count if TYPE = J, so for ID = 1, the count should be 2 rather than 3 since the first row is TYPE = K (ID = 2 where the count is 3).
Is this possible?
Perhaps conditional aggregation is what you want here:
SELECT
t.*,
COUNT(CASE WHEN TYPE = 'J' THEN 1 END) OVER (PARTITION BY ID) cnt
FROM TBL t
If you only wanted to display this count for those records actually having the J type, then we could try:
SELECT
t.*,
CASE WHEN TYPE = 'J'
THEN COUNT(CASE WHEN TYPE = 'J' THEN 1 END) OVER (PARTITION BY ID)
ELSE 0 END cnt
FROM TBL t

Aggregation for multiple SQL SELECT statements

I've got a table TABLE1 like this:
|--------------|--------------|--------------|
| POS | TYPE | VOLUME |
|--------------|--------------|--------------|
| 1 | A | 34 |
| 2 | A | 2 |
| 1 | A | 12 |
| 3 | B | 200 |
| 4 | C | 1 |
|--------------|--------------|--------------|
I want to get something like this (TABLE2):
|--------------|--------------|--------------|--------------|--------------|
| POS | Amount_A | Amount_B | Amount_C | Sum_Volume |
|--------------|--------------|--------------|--------------|--------------|
| 1 | 2 | 0 | 0 | 46 |
| 2 | 1 | 0 | 0 | 2 |
| 3 | 0 | 1 | 0 | 200 |
| 4 | 0 | 0 | 1 | 1 |
|--------------|--------------|--------------|--------------|--------------|
My Code so far is:
SELECT
(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'A') AS [Amount_A]
,(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'B') AS [Amount_B]
,(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'C') AS [Amount_C]
,(SELECT SUM(VOLUME)
FROM TABLE AS [Sum_Volume]
INTO [TABLE2]
Now two Questions:
How can I include the distinction concerning POS?
Is there any better way to count each TYPE?
I am using MSSQLServer.
What you're looking for is to use GROUP BY, along with your Aggregate functions. So, this results in:
USE Sandbox;
GO
CREATE TABLE Table1 (Pos tinyint, [Type] char(1), Volume smallint);
INSERT INTO Table1
VALUES (1,'A',34 ),
(2,'A',2 ),
(1,'A',12 ),
(3,'B',200),
(4,'C',1 );
GO
SELECT Pos,
COUNT(CASE WHEN [Type] = 'A' THEN [Type] END) AS Amount_A,
COUNT(CASE WHEN [Type] = 'B' THEN [Type] END) AS Amount_B,
COUNT(CASE WHEN [Type] = 'C' THEN [Type] END) AS Amount_C,
SUM(Volume) As Sum_Volume
FROM Table1 T1
GROUP BY Pos;
DROP TABLE Table1;
GO
if you have a variable, and undefined, number of values for [Type], then you're most likely going to need to use Dynamic SQL.
your first column should be POS, and you'll GROUP BY POS.
This will give you one row for each POS value, and aggregate (COUNT and SUM) accordingly.
You can also use CASE statements instead of subselects. For instance, instead of:
(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'A') AS [Amount_A]
use:
COUNT(CASE WHEN TYPE = 'A' then 1 else NULL END) AS [Amount_A]

SQL Grouping entries with a different value

Let's assume I have a report that displays an ID and VALUE from different tables
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
4 | 2 | 0 |
5 | 2 | 0 |
My goal is to display this table with grouped IDs and VALUEs. My rule to grouping VALUEs would be "If VALUE contains atleast one '1' then display '1' otherwise display '0'".
My current SQL is (simplified)
SELECT
TABLE_A.ID,
CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END AS VALUE
FROM TABLE_A, TABLE_B, TABLE_C
GROUP BY
TABLE_A.ID
(CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END)
The output is following
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 2 | 0 |
Which is half way to the output I want
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 2 | 0 |
So my Question is: How do I extend my current SQL (or change it completely) to get my desired output?
If you are having only 0 and 1 as distinct values in FOREIGN_VALUE column then using max() function as mentioned by HoneyBadger in the comment will fulfill your requirement.
SELECT
ID,
MAX(FOREIGN_VALUE) AS VALUE
FROM (SELECT
ID,
CASE WHEN FOREIGN_VALUE = 1
THEN 1
ELSE 0
END AS FOREIGN_VALUE
FROM TABLE,
FOREIGN_TABLE)
GROUP BY
ID;
Assuming value is always 0 or 1, you can do:
select id, max(value) as value
from t
group by id;
If value can take on other values:
select id,
max(case when value = 1 then 1 else 0 end) as value
from t
group by id;

Finding nth row using sql

select top 20 *
from dbo.DUTs D
inner join dbo.Statuses S on d.StatusID = s.StatusID
where s.Description = 'Active'
Above SQL Query returns the top 20 rows, how can I get a nth row from the result of the above query? I looked at previous posts on finding the nth row and was not clear to use it for my purpose.
Thanks.
The row order is arbitrary, so I would add an ORDER BY expression. Then, you can do something like this:
SELECT TOP 1 * FROM (SELECT TOP 20 * FROM ... ORDER BY d.StatusID) AS d ORDER BY d.StatusID DESC
to get the 20th row.
You can also use OFFSET like:
SELECT * FROM ... ORDER BY d.StatusID OFFSET 19 ROWS FETCH NEXT 1 ROWS ONLY
And a third option:
SELECT * FROM (SELECT *, rownum = ROW_NUMBER() OVER (ORDER BY d.StatusID) FROM ...) AS a WHERE rownum = 20
I tend to use CTEs with the ROW_NUMBER() function to get my lists numbered in order. As #zambonee said, you'll need an ORDER BY clause either way or SQL can put them in a different order every time. It doesn't usually, but without ordering it yourself, you're not guaranteed to get the same thing twice. Here I'm assuming there's a [DateCreated] field (DATETIME NOT NULL DEFAULT GETDATE()), which is usually a good idea so you know when that record was entered. This says "give me everything in that table and add a row number with the most recent record as #1":
; WITH AllDUTs
AS (
SELECT *
, DateCreatedRank = ROW_NUMBER() OVER(ORDER BY [DateCreated] DESC)
FROM dbo.DUTs D
INNER JOIN dbo.Statuses S ON D.StatusID = S.StatusID
WHERE S.Description = 'Active'
)
SELECT *
FROM AllDUTs
WHERE AllDUTs.DateCreatedRank = 20;
SELECT * FROM (SELECT * FROM EMP ORDER BY ROWID DESC) WHERE ROWNUM<11
It's another sample:
SELECT * ,CASE WHEN COUNT(0)OVER() =ROW_NUMBER()OVER(ORDER BY number) THEN 1 ELSE 0 END IsNth
FROM (
select top 10 *
from master.dbo.spt_values AS d
where d.type='P'
) AS t
+------+--------+------+-----+------+--------+-------+
| name | number | type | low | high | status | IsNth |
+------+--------+------+-----+------+--------+-------+
| NULL | 0 | P | 1 | 1 | 0 | 0 |
| NULL | 1 | P | 1 | 2 | 0 | 0 |
| NULL | 2 | P | 1 | 4 | 0 | 0 |
| NULL | 3 | P | 1 | 8 | 0 | 0 |
| NULL | 4 | P | 1 | 16 | 0 | 0 |
| NULL | 5 | P | 1 | 32 | 0 | 0 |
| NULL | 6 | P | 1 | 64 | 0 | 0 |
| NULL | 7 | P | 1 | 128 | 0 | 0 |
| NULL | 8 | P | 2 | 1 | 0 | 0 |
| NULL | 9 | P | 2 | 2 | 0 | 1 |
+------+--------+------+-----+------+--------+-------+