I am looking for a solution for the following problem, which affects two tables. I already tried to search for the solution, but couldn't find the way to go.
single_value
| docId | siteNo | siteName | siteAccount | comment | docDate | extNo
---+------------+--------+---------------+-------------+---------+-------------------------+-------
1 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL
multi_value
| docId | field_no | row_no | value_char | value_date | value_num
---+------------+----------+--------+------------+------------+-----------
1 | T000000095 | 60 | 1 | NULL | NULL | 250.00
2 | T000000095 | 60 | 2 | NULL | NULL | -1.24
3 | T000000095 | 61 | 1 | Positive | NULL | NULL
4 | T000000095 | 61 | 2 | Negative | NULL | NULL
5 | T000000095 | 62 | 1 | NULL | NULL | 90000.00
6 | T000000095 | 62 | 2 | NULL | NULL | 688000.00
What I need is now an SQL statement which gives me an output like the following one for each row_no of the table multi_value for a specific docId:
| docId | siteNo | siteName | siteAccount | comment | docDate | extNo | amount | addInfo | costUnit
---+------------+--------+---------------+-------------+---------+-------------------------+-------|--------+----------+----------
1 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL | 250.00 | Positive | 90000.00
2 | T000000095 | 201060 | Main Location | 92400 | NULL | 2014-10-31 00:00:00.000 | NULL | -1.24 | Negative | 688000.00
It has to list all Information of the table 'single_value' and kind of transpose the values of the table 'multi_value'. The connection between both tables can be achieved via the docId.
The table 'multi_value' is designed in that way, that each field no only allows one specific column to be filled:
60 = value_num (amount)
61 = value_char (addInfo)
62 = value_num (costUnit)
What is the easiest way to achieve that? The table layout cannot be changed. For the transpose of the multi_value I already tried the following, but it doesn't work with the varchar datatype within the column value_char.
SELECT row_no
SUM(case when field_no = 60 then value_num else 0 end) as amount,
--(case when field_no = 61 then value_char else 0 end) as addInfo,
SUM(case when field_no = 62 then value_num else 0 end) as costUnit
FROM multi_value
WHERE docId = 'T000000095'
GROUP By
row_no
Many thanks in advance.
Thomas
Not very elegant, but works fine :)
SELECT row_no,
SUM(case when field_no = 60 then value_num else 0 end) as amount,
case SUM(case
when field_no = 61 then (
case value_char
when 'Positive' then 1
else 2 end
) else 0 end
) when 1 then 'Positive' else 'Negative' end as addInfo,
SUM(case when field_no = 62 then value_num else 0 end) as costUnit
FROM multi_value
GROUP By
row_no
UPDATE
WITH dist as
(
SELECT DISTINCT value_char
FROM multi_value
WHERE value_char is not null
)
, with_no as
(
select value_char, row_number() over(order by value_char) [no]
from dist
)
, [raw] as
(
SELECT m.row_no,
SUM(case when m.field_no = 60 then m.value_num else 0 end) as amount,
SUM(case when m.field_no = 61 then d.[no] else 0 end) as addInfo,
SUM(case when m.field_no = 62 then m.value_num else 0 end) as costUnit
FROM multi_value m
LEFT JOIN with_no as d on d.value_char = m.value_char
GROUP By
m.row_no
)
SELECT row_no
, amount
, d.value_char as addInfo
, costUnit
FROM [raw] r
LEFT JOIN with_no as d on d.[no] = r.addInfo
Related
I managed to find a solution for formatting the shown driver table. The result is exactly what i need: One row for every QuoteID with the columns Birthdate and DriverType seperated by DriverIndex. My real drivertable has millions of rows.
Yet i am not convinced that this is the way to go. It just seems odd to me. But i am not a SQL expert. My Question: Can this be done in a more efficent way?
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE driver
([QuoteID] int, [DriverIndex] int,[Birthdate] date,[DriverType] int)
;
INSERT INTO driver
([QuoteID], [DriverIndex],[Birthdate], [DriverType])
VALUES
('72', '1','2022/01/01','11'),
('72', '2','2022/02/01','12'),
('73', '1','2022/03/01','13'),
('74', '1','2022/04/01','13'),
('73', '2','2022/05/01','10'),
('73', '3','2022/06/01','11');
Driver Table:
| QuoteID | DriverIndex | Birthdate | DriverType |
|---------|-------------|------------|------------|
| 72 | 1 | 2022-01-01 | 11 |
| 72 | 2 | 2022-02-01 | 12 |
| 73 | 1 | 2022-03-01 | 13 |
| 74 | 1 | 2022-04-01 | 13 |
| 73 | 2 | 2022-05-01 | 10 |
| 73 | 3 | 2022-06-01 | 11 |
Query:
with sq as(select QuoteID AS QuoteID_sq, [1] AS DriverIndex_1_DriverType , [2] AS DriverIndex_2_DriverType , [3] as DriverIndex_3_DriverType
from
( select [QuoteID], [DriverIndex],[Birthdate], [DriverType] from driver) src
pivot
( max([DriverType]) for DriverIndex in ([1], [2], [3]) ) piv),
sq2 as(select QuoteID as QuoteID_sq2, [1] AS DriverIndex_1_Birthdate , [2] AS DriverIndex_2_Birthdate , [3] as DriverIndex_3_Birthdate
from
( select [QuoteID], [DriverIndex],[Birthdate], [DriverType] from driver) src
pivot
( max([Birthdate]) for DriverIndex in ([1], [2], [3]) ) piv),
sq3 as(Select * from sq,sq2 Where sq.QuoteID_sq=sq2.QuoteID_sq2)
Select QuoteID_sq as QuoteID, max([DriverIndex_1_DriverType]) AS DriverIndex_1_DriverType,MAX([DriverIndex_2_DriverType]) AS DriverIndex_2_DriverType,Max([DriverIndex_3_DriverType]) AS DriverIndex_3_DriverType ,
max([DriverIndex_1_Birthdate]) AS DriverIndex_1_Birthdate , max([DriverIndex_2_Birthdate]) AS DriverIndex_2_Birthdate , max([DriverIndex_3_Birthdate]) as DriverIndex_3_Birthdate
from sq3
group by QuoteID_sq
Results:
| QuoteID | DriverIndex_1_DriverType | DriverIndex_2_DriverType | DriverIndex_3_DriverType | DriverIndex_1_Birthdate | DriverIndex_2_Birthdate | DriverIndex_3_Birthdate |
|---------|--------------------------|--------------------------|--------------------------|-------------------------|-------------------------|-------------------------|
| 72 | 11 | 12 | (null) | 2022-01-01 | 2022-02-01 | (null) |
| 73 | 13 | 10 | 11 | 2022-03-01 | 2022-05-01 | 2022-06-01 |
| 74 | 13 | (null) | (null) | 2022-04-01 | (null) | (null) |
You can simplify this by using only conditional aggregation.
SELECT QuoteID
, MAX(CASE WHEN DriverIndex = 1 THEN DriverType END) AS DriverIndex_1_DriverType
, MAX(CASE WHEN DriverIndex = 2 THEN DriverType END) AS DriverIndex_2_DriverType
, MAX(CASE WHEN DriverIndex = 3 THEN DriverType END) AS DriverIndex_3_DriverType
, MAX(CASE WHEN DriverIndex = 1 THEN Birthdate END) AS DriverIndex_1_Birthdate
, MAX(CASE WHEN DriverIndex = 2 THEN Birthdate END) AS DriverIndex_2_Birthdate
, MAX(CASE WHEN DriverIndex = 3 THEN Birthdate END) AS DriverIndex_3_Birthdate
FROM driver
GROUP BY QuoteID
ORDER BY QuoteID;
QuoteID
DriverIndex_1_DriverType
DriverIndex_2_DriverType
DriverIndex_3_DriverType
DriverIndex_1_Birthdate
DriverIndex_2_Birthdate
DriverIndex_3_Birthdate
72
11
12
null
2022-01-01
2022-02-01
null
73
13
10
11
2022-03-01
2022-05-01
2022-06-01
74
13
null
null
2022-04-01
null
null
Demo on db<>fiddle here
I have a table
/---------------------------------------\
|Region | Product | 1 | 2 | 3 | 4 |
|-------|---------|---|-----|-----|-----|
| A | ABC | 0 | 120 | 421 | 520 |
| B | ABC | 0 | 0 | 0 | 670 |
| C | DEF | 0 | 0 | 0 | 125 |
| D | PQR | 0 | 0 | 780 | 560 |
| E | PQR | 0 | 0 | 0 | 340 |
| F | XYZ | 0 | 0 | 0 | 780 |
| G | XYZ | 0 | 0 | 0 | 900 |
\---------------------------------------/
In this table, I need to find the name of products that were launched in quarter 4.
The result that query should give is DEF and XYZ
I will be grateful if someone could help
You need to group by product and aggregate (sum) the values for each quarter per product, regardless of region:
select
Product
from #table
group by Product
having sum([4]) > 0
and sum([3]) = 0
and sum([2]) = 0
and sum([1]) = 0
With sample data to illustrate:
create table #table
(
Region varchar(1),
Product varchar(3),
[1] int,
[2] int,
[3] int,
[4] int
)
insert into #table
values
('A','ABC',0,120,421,520),
('B','ABC',0,0,0,670),
('C','DEF',0,0,0,125),
('D','PQR',0,0,780,560),
('E','PQR',0,0,0,340),
('F','XYZ',0,0,0,780),
('G','XYZ',0,0,0,900)
select
Product
from #table
group by Product
having sum([4]) > 0
and sum([3]) = 0
and sum([2]) = 0
and sum([1]) = 0
drop table #table
Output:
/---------\
| Product |
|---------|
| DEF |
| XYZ |
\---------/
try this
select *
from yourTableName a
where a.field4 > 0
and a.field3 = 0
and a.field2 = 0
and a.field1 = 0
and a.product not in (select b.product
from yourTableName b
where b.field3 >0
or b.field2>0
or b.field1>0)
and if you just want the product use below
select a.product
from yourTableName a
where a.field4 > 0
and a.field3 = 0
and a.field2 = 0
and a.field1 = 0
and a.product not in (select b.product
from yourTableName b
where b.field3 >0
or b.field2>0
or b.field1>0)
here field4 as quarter 4
field3 as quarter 3 and so on.
I have a table that looks like the following:
+---------+----------+---------+-------+-----------+-----------+-------------+
| ValueId | ObjectId | Field | Value | Estimated | OrigValue | FromDefault |
+---------+----------+---------+-------+-----------+-----------+-------------+
| 1 | 1 | 'Stat1' | 35 | true | (null) | (null) |
| 2 | 1 | 'Stat2' | 2 | false | 0 | true |
| 3 | 1 | 'Stat3' | 0.213 | true | 0.212 | false |
| 4 | 2 | 'Stat1' | 513 | true | 122 | true |
| 5 | 2 | 'Stat2' | 31 | true | (null) | true |
| 6 | 2 | 'Stat3' | 2.411 | true | (null) | false |
+---------+----------+---------+-------+-----------+-----------+-------------+
Fiddle: http://www.sqlfiddle.com/#!9/445271/2/0
And I want the pivot(s) to look like this:
+----------+-------+-------+-------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+
| ObjectId | Stat1 | Stat2 | Stat3 | Stat1_Estimated | Stat1_OrigValue | Stat1_FromDefault | Stat2_Estimated | Stat2_OrigValue | Stat2_FromDefault | Stat3_Estimated | Stat3_OrigValue | Stat3_FromDefault |
+----------+-------+-------+-------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+
| 1 | 35 | 2 | 0.213 | true | (null) | (null) | false | false | true | true | 0.212 | false |
| 2 | 513 | 31 | 2.411 | true | 122 | true | true | (null) | true | true | (null) | false |
+----------+-------+-------+-------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+-----------------+-----------------+-------------------+
Fiddle: http://www.sqlfiddle.com/#!9/6e84ff2/2/0
I understand how to do the pivot to get the "value" as the field but not how I can include multiple pivots and get them to be named as as Field + _ + OriginalColumn
Edit: The number of distinct values for Field are well known as can be hard-coded into the answer.
SQL DEMO
Because the fields are know you can use conditional aggregation functions
SELECT ObjectId,
MAX(CASE WHEN Field = '''Stat1''' THEN Value END) as Stat1,
MAX(CASE WHEN Field = '''Stat2''' THEN Value END) as Stat2,
MAX(CASE WHEN Field = '''Stat3''' THEN Value END) as Stat3,
MAX(CASE WHEN Field = '''Stat1''' THEN Estimated END) as Stat_Estimated1,
MAX(CASE WHEN Field = '''Stat1''' THEN OrigValue END) as Stat_OrigValue1,
MAX(CASE WHEN Field = '''Stat1''' THEN FromDefault END) as Stat_FromDefault1,
MAX(CASE WHEN Field = '''Stat2''' THEN Estimated END) as Stat_Estimated2,
MAX(CASE WHEN Field = '''Stat2''' THEN OrigValue END) as Stat_OrigValue2,
MAX(CASE WHEN Field = '''Stat2''' THEN FromDefault END) as Stat_FromDefault2,
MAX(CASE WHEN Field = '''Stat3''' THEN Estimated END) as Stat_Estimated3,
MAX(CASE WHEN Field = '''Stat3''' THEN OrigValue END) as Stat_OrigValue3,
MAX(CASE WHEN Field = '''Stat3''' THEN FromDefault END) as Stat_FromDefault3
FROM mytable
GROUP BY ObjectId
;
You can also arrange the data and use the PIVOT function. Be aware value column can only contain one data type so I convert boolean to 0/1
SQL DEMO
SELECT ObjectId, Field, Value
FROM myTable
UNION ALL
SELECT ObjectId, CONCAT(Field, '_Estimated'), CASE WHEN Estimated IS NULL THEN NULL
WHEN Estimated THEN 1
ELSE 0
END
FROM myTable
UNION ALL
SELECT ObjectId, CONCAT(Field, '_OrigValue'), OrigValue
FROM myTable
UNION ALL
SELECT ObjectId, CONCAT(Field, '_FromDefault'), CASE WHEN FromDefault IS NULL THEN NULL
WHEN FromDefault THEN 1
ELSE 0
END
FROM myTable
;
I have two tables
1) Document: which represent a document
+----+----------+------+
| ID | Body | Type |
+----+----------+------+
| 1 | Ramesh | 1 |
| 2 | Khilan | 1 |
| 3 | kaushik | 4 |
| 4 | Chaitali | 2 |
| 5 | Hardik | 2 |
+----+----------+------+
2) Destination: which represent a party of the document
+--------+------------+--------+
| UserId | DocumentId | Status |
+--------+------------+--------+
| 6 | 3 | 4 |
| 4 | 5 | 5 |
| 89 | 2 | 0 |
| 15 | 4 | 3 |
| 89 | 1 | 0 |
+--------+------------+--------+
The status column represent a folder for the user, i want to get the count for each type for each folder, even if the folder is empty for a specifi user,
however if want them in this from,
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
| 89 | 3 | 0 | 0 | 0 |
| 89 | 4 | 0 | 0 | 0 |
| 89 | 5 | 0 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+
the issue I'm facing is I can't find a way to get the types the user does not have by join, i can get them using CASE but not in the form i want
my query is:
`SELECT dd.[Status],
SUM(CASE WHEN d.[Type] = 1 THEN 1 ELSE 0 END) AS 'Type1Count'
SUM(CASE WHEN d.[Type] = 2 THEN 1 ELSE 0 END) AS 'Type2Count'
SUM(CASE WHEN d.[Type] = 4 THEN 1 ELSE 0 END) AS 'Type4Count'
FROM [User] u LEFT JOIN [Destination] dd ON u.[Id] = dd.[UserId]
LEFT JOIN [Document] d ON dd.[DocumentId] = d.[Id]
WHERE u.[Id] = #UserId`
the result is
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+
So join all users onto a table of all statuses (I have named this Folder as per you description in the question) before you then join to Document and Destination:
SELECT u.UserId, st.Status,
SUM(CASE WHEN doc.Type = 1 THEN 1 ELSE 0 END) AS [Type 1 Count],
SUM(CASE WHEN doc.Type = 2 THEN 1 ELSE 0 END) AS [Type 2 Count],
SUM(CASE WHEN doc.Type = 4 THEN 1 ELSE 0 END) AS [Type 4 Count]
FROM User u
CROSS JOIN Folder st
LEFT OUTER JOIN Destination d
ON d.UserId = u.UserId
AND d.Status = st.Status
LEFT OUTER JOIN Document doc
ON doc.ID = d.DocumentId
GROUP BY u.UserId, st.Status
ORDER BY u.UserId
I am currently having difficulty getting the correct values from my table. Here is my table
NOTE: The column Status has 3 possible values (Cleaned, Unclean, Closed)
+-----------+-------------+--------+------------+
|ApplicantID|ApplicantName| Status | HireDate |
+-----------+-------------+--------+------------+
| 1 | John Smith |Cleaned |08/26/2015 |
| 2 | Alex Murphy |Closed |09/12/2015 |
| 3 | Oliver David|Cleaned |01/11/2015 |
| 4 | Max Payne |Unclean |03/18/2015 |
+-----------+-------------+--------+------------+
The output I'm expecting and it should also be sorted by year.
For example I call all these records for the year 2015 which I get using the variable #Year.
NOTE: The column Total is the SUM of Cleaned and Unclean
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January| 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October| 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
I can't seem to get the right code, for the sql this is my current code.
SELECT Month(HireDate) AS Month, COUNT(*)
FROM Hires
GROUP BY Month(HireDate)
I know my coding is wrong, because it is incomplete.
Generate a list of numbers from 1 to 12 first to hold all months. Then do a LEFT JOIN on Hires to make sure all missing months are accounted for. Then use conditional aggregation for the totals:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
)
SELECT
Month = DATENAME(MONTH, DATEADD(MONTH, N-1,0)),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteMonths m
LEFT JOIN Hires h
ON m.N = MONTH(h.HireDate)
--AND YEAR(h.HireDate) = #year --uncomment this line to filter for year.
GROUP BY m.N
ORDER BY m.N
If you want to include the YEAR:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteYears(yr) AS(
SELECT DISTINCT YEAR(HireDate) FROM Hires
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, y.yr - 1900, 0))
FROM CteMonths m
CROSS JOIN CteYears y
)
SELECT
Year = YEAR(d.dt),
Month = DATENAME(MONTH, d.dt),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteAllDates d
LEFT JOIN Hires h
ON MONTH(d.dt) = MONTH(h.HireDate)
AND YEAR(d.dt) = YEAR(h.HireDate)
GROUP BY YEAR(d.dt), MONTH(d.dt), DATENAME(MONTH, d.dt)
ORDER BY YEAR(d.dt), MONTH(d.dt)
If you want to filter for year, say #year = 2015, you can replace the previous ctes with:
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, #year - 1900, 0))
FROM CteMonths m
)...
I suggest to create TEMP table with values from 1 to 12 (numbers of months) and JOIN your table with TEMP table. To achieve values as columns names you can use PIVOT or CASE. You can do It in following:
INSERT INTO #Months VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
SELECT DATENAME(MONTH, DATEADD(MONTH, m.Id-1, 0)) AS [Month]
, SUM(CASE WHEN [Status] = 'Cleaned' THEN 1 ELSE 0 END) AS [Cleaned]
, SUM(CASE WHEN [Status] = 'Closed' THEN 1 ELSE 0 END ) AS [Closed]
, SUM(CASE WHEN [Status] = 'Unclean' THEN 1 ELSE 0 END) AS [Unclean]
, SUM(CASE WHEN [Status] IN ('Unclean', 'Cleaned') THEN 1 ELSE 0 END) AS [Total]
FROM #Test t
RIGHT JOIN #Months m ON m.Id = MONTH(t.HireDate)
GROUP BY m.Id
OUTPUT
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January | 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October | 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
DEMO
You can test It at: SQL FIDDLE