I am running into some trouble trying to pivot some data out of SQL.
I have three tables that will comprise the data.
Table 1: (Clause)
-Clause
-ClauseName
Table 2: (Process)
-Id
-ProcessName
Table 3: (RELProcessClauses)
-ProcessId
-Clause
-WeightedValue
Ultimately, I am looking to have a matrix of data that is Clause, ClauseName down the left, ProcessName across the top and the Weighted value to correspond between Process and Clause.
Not sure if this will make much sense.
Join the three tables and use PIVOT on it. You can run the following query:
SELECT * FROM (
SELECT
c.Clause,
c.ClauseName,
p.ProcessName,
pc.WeightedValue
from RELProcessClauses pc
JOIN Clause c on pc.clause = c.clause
JOIN Process p on pc.ProcessId = p.id
) x
PIVOT (
SUM(WeightedValue)
FOR ProcessName IN ([ProcessName1], [ProcessName2], [ProcessName3])
) as pvt
Output table:
+--------+-------------+--------------+--------------+--------------+
| Clause | ClauseName | ProcessName1 | ProcessName2 | ProcessName3 |
+--------+-------------+--------------+--------------+--------------+
| 1 | ClauseName1 | 10 | 15 | 30 |
| 2 | ClauseName2 | 15 | 20 | 30 |
| 3 | ClauseName3 | 20 | 20 | 30 |
+--------+-------------+--------------+--------------+--------------+
The query/output works on the demo tables created using the query below:
CREATE TABLE Clause (
Clause int,
ClauseName varchar(255)
);
CREATE TABLE Process (
Id int,
ProcessName varchar(255)
);
CREATE TABLE RELProcessClauses (
ProcessId int,
Clause int,
WeightedValue int
);
INSERT INTO Clause VALUES
(1, 'ClauseName1'),
(2, 'ClauseName2'),
(3, 'ClauseName3');
INSERT INTO Process VALUES
(1, 'ProcessName1'),
(2, 'ProcessName2'),
(3, 'ProcessName3');
INSERT INTO RELProcessClauses VALUES
(1, 1, 10),
(1, 2, 15),
(1, 3, 20),
(2, 1, 15),
(2, 2, 20),
(2, 3, 20),
(3, 1, 30),
(3, 2, 30),
(3, 3, 30);
Related
I have an Item table:
Id | Title | Active
====================
1 | Item 1 | 1
2 | Item 2 | 1
A Location table:
Id | Name
=========
1 | A1
2 | B1
and a link table, where EventId specifies a cycle count event:
Id | EventId | ItemId | LocationId
=============|====================
1 | 1 | 1 | 2
2 | 1 | 2 | 1
3 | 2 | 1 | 1
4 | 2 | 2 | 2
5 | 3 | 1 | 1
I need to determine what items haven't been cycle-counted for a specified EventId (which in this example would be ItemId 2 for EventId 3). We're using a code generation tool that only supports tables and views with a simple filter, so I can't use a sproc or table-valued function. Ideally we'd like to be to do this:
SELECT [EventId], [ItemId] FROM [SomeView] WHERE [EventId] = 3
and get a result like
EventId | ItemId
================
3 | 2
I've tried to wrap my head around this -- unsuccessfully -- because I know it's difficult to query a negative. Is this even possible?
Is something like the following what you're after?
select l.eventId, x.Id ItemId
from Link l
cross apply (
select *
from Items i
where i.Id != l.ItemId
)x
where l.EventId = 3;
--data to work with
DECLARE #items TABLE (ID int, Title nvarchar(100), Active int)
INSERT INTO #items VALUES (1, 'Item 1', 1)
INSERT INTO #items VALUES (2, 'Item 2', 1)
DECLARE #location TABLE (ID int, Name nvarchar(100))
INSERT INTO #location VALUES (1, 'A1')
INSERT INTO #location VALUES (2, 'B1')
DECLARE #linkTable TABLE (ID int, EventId int, ItemId int, LocationId int)
INSERT INTO #linkTable VALUES (1, 1, 1, 2)
INSERT INTO #linkTable VALUES (2, 1, 2, 1)
INSERT INTO #linkTable VALUES (3, 2, 1, 1)
INSERT INTO #linkTable VALUES (4, 2, 2, 2)
INSERT INTO #linkTable VALUES (5, 3, 1, 1)
INSERT INTO #linkTable VALUES (6, 4, 2, 1)
--query you want
SELECT 3 as EventID, ID as ItemID
FROM #items i
WHERE ID not in (SELECT ItemId
FROM #linkTable
WHERE EventId = 3)
Get all the ItemIDs from the LinkTable and then get all the items from the Items table that dont have the sync event. You can replace the 3 in WHERE and SELECT clauses with whatever event you are looking for. And if you want all such pairs of event + item then this should do it:
SELECT subData.EventId, subData.ItemID
FROM (SELECT i.ID as ItemID, cj.EventId
FROM #items i CROSS JOIN (SELECT DISTINCT EventId
FROM #linkTable) cj) subData
left join #linkTable lt ON lt.EventId = subData.EventId and lt.ItemId = subData.ItemID
WHERE lt.ID is null
This could be heavy on performance because CROSS JOIN and DISTINCT and subjoins but it gets the job done. At 1st you create a data of all possible items and events pairs, then left join linked table to it and if the linked table's ID is null that means that there is no event + item pair which means that the item is not synced for that event.
I want to define variables before a CTE table and after a CTE table because some variables are dependent on the result of the CTE table. For example
SET(K,B) = (5,2);
with my_data(Key,Index,Value) as (
-- data table as cte
select * from values
(1, 3, 10),
(1, 5, 18),
(1, 14, 4),
(2, 2, 11),
(2, 13, 24),
(2, 29, 40)
)
SELECT VALUE + $K
FROM my_data
This examples works perfectly. But this code:
SET(K,B) = (5,2);
with my_data(Key,Index,Value) as (
-- data table as cte
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SET AVG_VAL = (SELECT AVG(VALUE) FROM my_data);
SELECT VALUE + $AVG_VAL
FROM my_data
doesn't because snowflake gives me this error
"SQL compilation error: syntax error line 34 at position 0 unexpected 'SET'."
Should I create a temporary table to store the result of this query (SELECT AVG(VALUE) FROM my_data) in it and then include/use this temporary table for future queries instead of a variable?
Your "CTE" is not a standalone "thing" it only exist in the context of a SELECT.
Thus
WITH cte_x AS (...)
SELECT * FROM cte_x
is one SELECT which has a CTE attached to it.
Thus for you variable assignment the CTE has to be "IN" the paren's
with my_data(Key,Index,Value) as (
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SELECT AVG(VALUE) FROM my_data;
AVG(VALUE)
17.833333
given that is a discrete chunk of SQL, that can be captured into the variable:
set AVG_VAL = (
with my_data(Key,Index,Value) as (
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SELECT AVG(VALUE) FROM my_data
);
status
Statement executed successfully.
now we can use that value:
select $AVG_VAL * 2;
$AVG_VAL * 2
35.666666
But the next query:
SELECT VALUE + $AVG_VAL
FROM my_data
002003 (42S02): SQL compilation error:
Object 'MY_DATA' does not exist or not authorized.
has no CTE called my_data, so that need to be insert:
with my_data(Key,Index,Value) as (
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SELECT VALUE + $AVG_VAL
FROM my_data
If you want a table that can be "used twice" you will need an actual table, at which point I would suggest a temporary table so it only have context in this session.
Which the nature of Pankaj's answer (ether via a permanent or temp table)
This can be done as in -
select * from d2;
+-----+-----+
| ID1 | ID2 |
|-----+-----|
| 1 | 2 |
| 100 | 2 |
| 3 | 4 |
| 300 | 4 |
+-----+-----+
Setting variable -
set (var1) = (select sum(id2) from d2);
+----------------------------------+
| status |
|----------------------------------|
| Statement executed successfully. |
+----------------------------------+
Using variable -
select id1+$var1 from d2;
+-----------+
| ID1+$VAR1 |
|-----------|
| 13 |
| 112 |
| 15 |
| 312 |
+-----------+
An alternatvie approach is to simply use windowed AVG function:
with my_data(Key,Index,Value) as (
-- data table as cte
select * from values
(1, 3, 10),
(1, 5, 18),
(1, 14, 4),
(2, 2, 11),
(2, 13, 24),
(2, 29, 40)
)
SELECT VALUE, AVG(VALUE) OVER(),
VALUE + AVG(VALUE) OVER()
FROM my_data;
Output:
OVER() means that the window used to compute average spans over all rows.
I want to select all users from the users' table and their data from the vacation table as start vacation date and end date to get their vacation balance and if the user has no vacation taken return 0
Here is my query:
SELECT
dbo.USERINFO.USERID,
dbo.USERINFO.NAME AS إسم_الموظف,
SUM(DATEDIFF(d, dbo.vacation.STARTSDAY, dbo.vacation.ENDSDAY) + 1) AS عددالايام,
dbo.userinfo.balance - (SUM(DATEDIFF(d, dbo.vacation.STARTSDAY, dbo.vacation.ENDSDAY) + 1)) AS الرصيد
FROM
dbo.USERINFO
LEFT JOIN
dbo.Vacation ON dbo.Vacation.UserID = dbo.USERINFO.USERID
WHERE
dbo.USERINFO.DEFAULTDEPTID <> 7
AND dbo.vacation.DATEID = 2
AND year(dbo.vacation.STARTSDAY) = 2020
AND month(dbo.vacation.STARTSDAY) BETWEEN 1
AND 12
GROUP BY
dbo.USERINFO.userid,
dbo.USERINFO.NAME,
dbo.userinfo.balance
You need to move the condition inside the case when statement while calculating the sum
SELECT U.USERID,
U.NAME AS إسم_الموظف,
ISNULL(SUM(CASE WHEN V.DATEID = 2
AND year(V.STARTSDAY) = 2020
THEN DATEDIFF(d, V.STARTSDAY, V.ENDSDAY)
ELSE 0
END), 0) AS عددالايام,
ISNULL(U.balance, 0) - ISNULL(SUM(CASE WHEN V.DATEID = 2
AND year(V.STARTSDAY) = 2020
THEN DATEDIFF(d, V.STARTSDAY, V.ENDSDAY)
ELSE 0
END), 0) AS الرصيد
FROM dbo.USERINFO AS U
LEFT JOIN dbo.Vacation AS V ON V.UserID = U.USERID
WHERE U.DEFAULTDEPTID <> 7
GROUP BY U.userid,
U.NAME,
U.balance
Results:
+--------+------------+-----------+--------+
| USERID | إسم_الموظف | عددالايام | الرصيد |
+--------+------------+-----------+--------+
| 1 | NAME 1 | 8 | 2 |
+--------+------------+-----------+--------+
| 2 | NAME 2 | 6 | 4 |
+--------+------------+-----------+--------+
| 4 | NAME 4 | 0 | 10 |
+--------+------------+-----------+--------+
The script used to generate the dummy data is:
CREATE TABLE USERINFO (USERID int, NAME VARCHAR(20), BALANCE INT, DEFAULTDEPTID INT)
INSERT INTO USERINFO VALUES
(1, 'NAME 1', 10, 5),
(2, 'NAME 2', 10, 4),
(3, 'NAME 3', 10, 7),
(4, 'NAME 4', 10, 5)
CREATE TABLE Vacation (UserID INT, STARTSDAY DATETIME, ENDSDAY DATETIME, DATEID INT)
INSERT INTO Vacation VALUES
(1, '2020-01-12', '2020-01-20', 2),
(1, '2020-01-22', '2020-01-24', 3),
(2, '2020-01-27', '2020-01-31', 2),
(2, '2020-03-27', '2020-03-29', 2),
(7, '2020-03-27', '2020-03-29', 2)
Note that the UserId 3 is retired so it is not included in the result.
Note that vacation with DateId 3 not included when calculating the sum
I have below table (to simplify I only show a piece of the tables as an example, not all their content):
CREATE TABLE InstArtRel
(
id INT IDENTITY(1, 1) NOT NULL,
idIns INT,
ExpDateRev DATE,
codArticle NVARCHAR(4),
PRIMARY KEY (id)
);
INSERT INTO InstArtRel (idIns, ExpDateRev, codArticle)
VALUES (17400, datefromparts(2018, 10, 1), 'X509'),
(17400, datefromparts(2020, 12, 2), 'X529'),
(17400, datefromparts(2016, 9, 10), 'T579'),
(17400, datefromparts(2017, 6, 7), 'Z669'),
(10100, datefromparts(2019, 8, 17), 'TG09'),
(10100, datefromparts(2018, 3, 28), 'TG09'),
(10100, datefromparts(2018, 4, 24), 'TG09'),
(10100, datefromparts(2016, 7, 12), 'TG09');
CREATE TABLE Installations
(
idIns INT NOT NULL,
DateIns DATETIME,
PRIMARY KEY (idIns)
);
INSERT INTO Installations (idIns, DateIns)
VALUES (17400, '2020-12-01'),
(10100, '2022-05-07');
For each idIns in table Installations I need to update its DateIns column with the ExpDateRev column in InstArtRel table based on the following assumptions:
If all codArticle column values for an IdIns in InstArtRel table are the same, then DateIns column in table Installations will be updated for the corresponding idIns with the maximum value of ExpDateRev.
Otherwise, if all codArticle column values are NOT the same for an IdIns in InstArtRel table, then the DateIns column in table Installations will be updated for the corresponding idIns with the minimun value of ExpDateRev.
Better an example... taken into account that said above, the result in this case will be:
idIns | DateIns
------+-----------
17400 | 2016-9-10
10100 | 2019-8-17
The Aggregate with CASE will help you.
Query:
SELECT idIns,CASE WHEN COUNT(DISTINCT codArticle) = 1 THEN MAX(ExpDateRev)
WHEN COUNT(DISTINCT codArticle) != 1 THEN MIN(ExpDateRev) END DateIns
FROM InstArtRel
GROUP BY idIns
Output:
| idIns | DateIns |
|-------|------------|
| 10100 | 2019-08-17 |
| 17400 | 2016-09-10 |
UPDATE Query:
UPDATE I
SET I.DateIns = R.DateIns
FROM Installations I
JOIN (
SELECT idIns,CASE WHEN COUNT(DISTINCT codArticle) = 1 THEN MAX(ExpDateRev)
WHEN COUNT(DISTINCT codArticle) != 1 THEN MIN(ExpDateRev) END DateIns
FROM InstArtRel
GROUP BY idIns
)R ON R.idIns = I.idIns
SQL Fiddle link
Microsoft SQL Server 2012 (SP1) - 11.0.3156.0 (X64)
I am not sure of the best way to word this and have tried a few different searches with different combinations of words without success.
I only want to Sum Sequence = 1 when there are Sequence > 1, in the table below the Sequence = 1 lines marked with *. I don't care at all about checking that Sequence 2,3,etc match the same pattern because if they exist at all I need to Sum them.
I have data that looks like this:
| Sequence | ID | Num | OtherID |
|----------|----|-----|---------|
| 1 | 1 | 10 | 1 |*
| 2 | 1 | 15 | 1 |
| 3 | 1 | 20 | 1 |
| 1 | 2 | 10 | 1 |*
| 2 | 2 | 15 | 1 |
| 1 | 3 | 10 | 1 |
| 1 | 1 | 40 | 3 |
I need to sum the Num column but only when there is more than one sequence. My output would look like this:
Sequence Sum OtherID
1 20 1
2 30 1
3 20 1
I have tried grouping the queries in a bunch of different ways but really by the time I get to the sum, I don't know how to look ahead to make sure there are greater than 1 sequences for an ID.
My query at the moment looks something like this:
select Sequence, Sum(Num) as [Sum], OtherID
from tbl
where ID in (Select ID from tbl where Sequence > 1)
Group by Sequence, OtherID
tbl is a CTE that I wrapped around my query and it partially works, but is not really the filter I wanted.
If this is something that just shouldn't be done or can't be done then I can handle that, but if it's something I am missing I'd like to fix the query.
Edit:
I can't give the full query here but I started with this table/data (to get the above output). The OtherID is there because the data has the same ID/Sequence combinations but that OtherID helps separate them out so the rows are not identical (multiple questions on a form).
Create table #tmpTable (ID int, Sequence int, Num int, OtherID int)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 1, 10, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 2, 15, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 3, 20, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (2, 1, 10, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (2, 2, 15, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (3, 1, 10, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 1, 40, 3)
The following will sum over Sequence and OtherID, but only when:
Either
sequence is greater than 1
or
there is something else with the same ID and OtherID, but a different sequence.
Query:
select Sequence, Sum(Num) as SumNum, OtherID from #tmpTable a
where Sequence > 1
or exists (select * from #tmpTable b
where a.ID = b.ID
and a.OtherID = b.OtherID
and b.Sequence <> a.Sequence)
group by Sequence, OtherID;
It looks like you are trying to sum by Sequence and OtherID if the Count of ID >1, so you could do something like below:
select Sequence, Sum(Num) as [Sum], OtherID
from tbl
where ID in (Select ID from tbl where Sequence > 1)
Group by Sequence, OtherID
Having count(id)>1