SSMS Case Statement With Multiple Fields - case

I have a case statement to sum, round and label amounts that works fine, but the data ends up in horizontal format (multiple money amounts per record), causing me to do an unpivot in a subsequent statement to format the data vertically (one money amount per record). I would like to accomplish this in one statement if possible. My code is as follows:
SELECT
Field,
ROUND(SUM(CASE
WHEN TYPE = 'Paid Loss'
THEN AMOUNT
ELSE 0
END
), 2
) PAID,
ROUND(SUM(CASE
WHEN TYPE = 'OS'
THEN AMOUNT
ELSE 0
END
), 2
) OS,
ROUND(SUM(CASE
WHEN TYPE <> 'Paid Exp'
THEN AMOUNT
ELSE 0
END
), 2
) INCURRED
FROM dbo.mydatabase
GROUP BY Field;
The result is:
Field |PAID |OS |INCURRED
----------------------------
result1 | 1 | 20 | 10
result2 | 5 | 30 | 15
When what I really want is:
Field | DATA_TYPE | AMOUNT
---------------------------
result1 | PAID | 1
result2 | PAID | 5
result3 | OS | 20
result4 | OS | 30
result5 | INCURRED | 10
result6 | INCURRED | 15
Keys will be unique so that isn't an issue. Anyone know how to rearrange the CASE so this can be done in one statement? Thanks!

Wouldn't this work?
SELECT Field, 'PAID' DATA_TYPE, ROUND(SUM(AMOUNT), 2) AMOUNT
FROM dbo.mydatabase
WHERE TYPE = 'Paid Loss'
GROUP BY Field
UNION ALL
SELECT Field, 'OS' DATA_TYPE, ROUND(SUM(AMOUNT), 2) AMOUNT
FROM dbo.mydatabase
WHERE TYPE = 'OS'
GROUP BY Field
UNION ALL
SELECT Field, 'INCURRED' DATA_TYPE, ROUND(SUM(AMOUNT), 2) AMOUNT
FROM dbo.mydatabase
WHERE TYPE <> 'Paid Exp'
GROUP BY Field
or even like this:
SELECT Field
, (CASE
WHEN TYPE IN ('Paid Loss', 'OS') THEN TYPE
WHEN TYPE <> 'Paid Exp' THEN 'INCURRED'
END) DATA_TYPE
, ROUND(SUM(AMOUNT), 2) AMOUNT
FROM dbo.mydatabase
GROUP BY Field
, (CASE
WHEN TYPE IN ('Paid Loss', 'OS') THEN TYPE
WHEN TYPE <> 'Paid Exp' THEN 'INCURRED'
END) DATA_TYPE

Related

SQLite multiple CASE WHEN weird result

I'm using multiple CASE WHEN to find device actions in selected days, but instead of getting only the abreviation names (like V or C), sometimes i get the full action name. If i try to replace the 'ELSE action' with ELSE '', i get some blanks, even though there aren't any blank actions... How can i improve my query?
SELECT device,
CASE
WHEN action='Vaccum' AND strftime('%d', timestamp_action) = '25' THEN 'V'
WHEN action='Cooling' AND strftime('%d', timestamp_action) = '25' THEN 'C' ELSE action END AS '25',
CASE
WHEN action='Vaccum' AND strftime('%d', timestamp_action) = '26' THEN 'V'
WHEN action='Cooling' AND strftime('%d', timestamp_action) = '26' THEN 'C' ELSE action END AS '26',
FROM diary WHERE strftime('%m', timestamp_action = '08')
GROUP BY device
ORDER BY device
I want to get the latest action on selected days of all devices. I have around 100 devices and i need the actions for the entire month.
Example table:
timestamp_action | device | action
------------------------+---------------+-----------
2022-08-25 11:08 | 1 | Cooling
2022-08-25 11:09 | 1 | Vaccum
2022-08-25 11:08 | 2 | Cooling
2022-08-26 11:10 | 2 | Vaccum
2022-08-26 11:11 | 2 | Cooling
2022-08-26 12:30 | 1 | Vaccum
So the result i'm looking for is:
device | 25 | 26 .....
-----------+-----------+--------------
1 | V | V
2 | C | C
Use 2 levels of aggregation:
WITH cte AS (
SELECT device,
strftime('%d', timestamp_action) day,
CASE action WHEN 'Vaccum' THEN 'V' WHEN 'Cooling' THEN 'C' ELSE action END action,
MAX(timestamp_action) max_timestamp_action
FROM diary
WHERE strftime('%Y-%m', timestamp_action) = '2022-08'
GROUP BY device, day
)
SELECT device,
MAX(CASE WHEN day = '25' THEN action END) `25`,
MAX(CASE WHEN day = '26' THEN action END) `26`
FROM cte
GROUP BY device;
See the demo.

SQL to find next greater records for each element

I've got a table defined like this:
CREATE TABLE event (t REAL, event TEXT, value);
For each record in the table which have event='type' and value='G' there will be two corresponding records with event='Z' - one with value=1 and one with value=0. Here is an example:
t | event | value
1624838448.123 | type | G
1624838448.123 | Z | 1
1624839543.215 | Z | 0
Note that there could be other event='Z' records that don't have corresponding type='G' records. I'm trying to write a query to find all the event='G' records that do have a corresponding type='G' record to use as the bounds for an additional query (or join?).
Note: The t value for the "type" event and the Z event where value=1 will always be the same.
So for instance if the table looked like this:
t | event | value
1624838448.123 | type | G
1624838448.123 | Z | 1
1624839543.215 | Z | 0
1624839555.555 | type | H
1624838555.555 | Z | 1
1624839602.487 | Z | 0
1624839999.385 | type | G
1624839999.385 | Z | 1
1624840141.006 | Z | 0
Then I want the results of the query to return this:
t1 | t2
1624838448.123 | 1624839543.215
1624839999.385 | 1624840141.006
From your comment:
There are always three records (ignoring any other events in between)
in chronological order: the "type" event, the first "Z" record with
the same timestamp, and the second "Z" record with a later timestamp
So, there is no need to return t1 separately since it is equal to t in the row where event = 'type' and value = 'G'.
For t2 you can use conditional aggregation with MIN() window function:
SELECT t1, t2
FROM (
SELECT t AS t1, event, value
MIN(CASE WHEN event = 'Z' AND value = '0' THEN t END) OVER (ORDER BY t ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) t2
FROM Event
)
WHERE event = 'type' AND value = 'G'
See the demo.
I found a solution using the RANK() function. With this I get an intermediate table which has the same rank for both the "type" and first "Z" record, since they have the same timestamp, and a rank two greater for the second "Z" record. I use WITH so I can self join repeatedly without having to specify the same query over and over. I first join the "type" and first "Z" row by requiring that the type of two second record be greater than that of the first (so I only get the type:Z combination and not type:type, Z:type, or Z:Z). Then I self join again to get the rank-2 row which picks up the second Z record. Overall, the query looks like this:
WITH Seq(t,event,A,I)
AS
(
SELECT t, event, value,
RANK() OVER (ORDER BY t) I
FROM Event e1
WHERE (e1.event='type' OR e1.event='Z')
)
SELECT s2.t,s3.t
FROM Seq s1
INNER JOIN Seq s2 ON s1.I = s2.I AND s1.event < s2.event
INNER JOIN Seq s3 ON s1.I = s3.I-2
WHERE s1.value='G';

Is there a way to reuse subqueries in the same query?

See Update at end of question for solution thanks to marked answer!
I'd like to treat a subquery as if it were an actual table that can be reused in the same query. Here's the setup SQL:
create table mydb.mytable
(
id integer not null,
fieldvalue varchar(100),
ts timestamp(6) not null
)
unique primary index (id, ts)
insert into mydb.mytable(0,'hello',current_timestamp - interval '1' minute);
insert into mydb.mytable(0,'hello',current_timestamp - interval '2' minute);
insert into mydb.mytable(0,'hello there',current_timestamp - interval '3' minute);
insert into mydb.mytable(0,'hello there, sir',current_timestamp - interval '4' minute);
insert into mydb.mytable(0,'hello there, sir',current_timestamp - interval '5' minute);
insert into mydb.mytable(0,'hello there, sir. how are you?',current_timestamp - interval '6' minute);
insert into mydb.mytable(1,'what up',current_timestamp - interval '1' minute);
insert into mydb.mytable(1,'what up',current_timestamp - interval '2' minute);
insert into mydb.mytable(1,'what up, mr man?',current_timestamp - interval '3' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '4' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '5' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '6' minute);
What I want to do is return only rows where FieldValue differs from the previous row. This SQL does just that:
locking row for access
select id, fieldvalue, ts from
(
--locking row for access
select
id, fieldvalue,
min(fieldvalue) over
(
partition by id
order by ts, fieldvalue rows
between 1 preceding and 1 preceding
) fieldvalue2,
ts
from mydb.mytable
) x
where
hashrow(fieldvalue) <> hashrow(fieldvalue2)
order by id, ts desc
It returns:
+----+---------------------------------+----------------------------+
| id | fieldvalue | ts |
+----+---------------------------------+----------------------------+
| 0 | hello | 2015-05-06 10:13:34.160000 |
| 0 | hello there | 2015-05-06 10:12:34.350000 |
| 0 | hello there, sir | 2015-05-06 10:10:34.750000 |
| 0 | hello there, sir. how are you? | 2015-05-06 10:09:34.970000 |
| 1 | what up | 2015-05-06 10:13:35.470000 |
| 1 | what up, mr man? | 2015-05-06 10:12:35.690000 |
| 1 | what up, duder? | 2015-05-06 10:09:36.240000 |
+----+---------------------------------+----------------------------+
The next step is to return only the last row per ID. If I were to use this SQL to write the previous SELECT to a table...
create table mydb.reusetest as (above sql) with data;
...I could then do this do get the last row per ID:
locking row for access
select t1.* from mydb.reusetest t1,
(
select id, max(ts) ts from mydb.reusetest
group by id
) t2
where
t2.id = t1.id and
t2.ts = t1.ts
order by t1.id
It would return this:
+----+------------+----------------------------+
| id | fieldvalue | ts |
+----+------------+----------------------------+
| 0 | hello | 2015-05-06 10:13:34.160000 |
| 1 | what up | 2015-05-06 10:13:35.470000 |
+----+------------+----------------------------+
If I could reuse the subquery in my initial SELECT, I could achieve the same results. I could copy/paste the entire query SQL into another subquery to create a derived table, but this would just mean I'd need to change the SQL in two places if I ever needed to modify it.
Update
Thanks to Kristján, I was able to implement the WITH clause into my SQL like this for perfect results:
locking row for access
with items (id, fieldvalue, ts) as
(
select id, fieldvalue, ts from
(
select
id, fieldvalue,
min(fieldvalue) over
(
partition by id
order by ts, fieldvalue
rows between 1 preceding and 1 preceding
) fieldvalue2,
ts
from mydb.mytable
) x
where
hashrow(fieldvalue) <> hashrow(fieldvalue2)
)
select t1.* from items t1,
(
select id, max(ts) ts from items
group by id
) t2
where
t2.id = t1.id and
t2.ts = t1.ts
order by t1.id
Does WITH help? That lets you define a result set you can use multiple times in the SELECT.
From their example:
WITH orderable_items (product_id, quantity) AS
( SELECT stocked.product_id, stocked.quantity
FROM stocked, product
WHERE stocked.product_id = product.product_id
AND product.on_hand > 5
)
SELECT product_id, quantity
FROM orderable_items
WHERE quantity < 10;

How to bind multiples columns against a single row in gridview asp.net?

I am working on a bill customization project in which customer requirement is described below:
First there is an Invoice details table:
e.g. (Table Structure and values)
Particulars InvoiceNo InvoiceDate InvoiceQty UnitOfMeasurement Rate Amount Currency
Apple I-90 2-12-2014 1000 Kg 8 8000 INR
It can contain multiple entries ..but the InvoiceNo will be different
Now another table contains an invoice's Damage Details (for insurance purposes)
e.g. (Invoice Damage Details Table):
Particular InvoiceNo invoiceqty AffectedQty UOM Rate Remarks
Apple I-90 1000 100 Kg 8 Pressed so waste
Apple I-90 1000 500 Kg 8 Smelled And waste
Now a Particular can contain different reasons for different quantity as mentioned above.
The last and final table contains all Information and the customer is demanding the following format to provide for easy access for their clients and users:
-----------------------------------------------------------------------------------------
Particular | InvoiceNo | Date | Qty | Claimed | Assessed |
| | | |------------------------------------------------
| | | |Qty | Rate | Amount | Qty | Rate | Amount |
-----------------------------------------------------------------------------------------
|100 | 8 | 800 | 100 | 8 | 800
Apple | I-90 |2-12-2014 |1000 |500 | 8 | 4000 | 500 | 8 | 4000
-----------------------------------------------------------------------------------------
Total : 1000 | 600 4800 | 600 | 4800
Here I want to show total Sum of Qty (total ) i.e. 1000 Kg , Claimed Qty : 600
ClaimedAmount : 4800 .. Assessed Qty : 600 .. Assessed Amount : 4800
There might be assessed quantity which can be edited by the owner, and amount and rate may also vary, but the main and important thing is how to show a row against multiple columns of Claimed And Assessed Description.
Code behind Sql Storage is as follows:
ALTER PROCEDURE [dbo].[InsertTable] #claimID VARCHAR(25) = ''
,#Updateby VARCHAR(70) = ''
,#AffectedQty VARCHAR(30) = ''
,#myTableType MyTableType1 readonly
,#myInvoiceTable MyInvoiceTableNew readonly
AS
-- here MyTableType1 and MyInvoiceTableNew are user defined data types
BEGIN
BEGIN TRY
BEGIN TRANSACTION
DELETE
FROM InvoiceDetail
WHERE Claim_ID = #claimID
INSERT INTO InvoiceDetail (
Claim_ID
,Currency
,Particulars
,InvoiceNo
,InvoiceDate
,Invoice_Qty
,UOM
,Rate
,Amount
,UpdateBy
,UpdateDate
,STATUS
)
SELECT #claimID
,Currency
,Particulars
,InvoiceNo
,InvoiceDate
,Invoice_Qty
,UOM
,Rate
,Amount
,#Updateby
,convert(DATETIME, GETDATE(), 103)
,1
FROM #myInvoiceTable
DELETE
FROM Invoice_Damage_Detail
WHERE Claim_ID = #claimID
INSERT INTO Invoice_Damage_Detail (
Claim_ID
,Particulars
,InvoiceNo
,Invoice_Qty
,Affected_Qty
,UOM
,Rate
,Remarks
,UpdateBy
,UpdateDate
,STATUS
)
SELECT #claimID
,Particulars
,InvoiceNo
,Invoice_Qty
,Affected_Qty
,UOM
,Rate
,Remarks
,#Updateby
,convert(DATETIME, GETDATE(), 103)
,1
FROM #myTableType
DELETE
FROM Invoice_Final_Assessment_Details
WHERE ClaimID = #claimID
INSERT INTO Invoice_Final_Assessment_Details (
ClaimID
,Particular
,InvoiceNo
,InvoiceDate
,InvoiceQty
,ClaimedQty
,ClaimedRate
,ClaimedAmount
,ClaimedUOM
,AssessedQty
,AssessedRate
,AssessesAmount
,Createdate
,UpdateDate
,UpdatedBy
,IsDeleted
)
SELECT #claimID
,mt2.Particulars
,mt2.InvoiceNo
,mt2.InvoiceDate
,mt2.Invoice_Qty
,mt.Affected_Qty
,mt2.Rate
,(CONVERT(BIGINT, isnull(mt2.Invoice_Qty, 0)) * Convert(BIGINT, isnull(mt.Rate, 0))) AS ClaimedAmount
,mt2.UOM
,mt.Affected_Qty
,mt.Rate
,(CONVERT(BIGINT, isnull(mt.Affected_Qty, 0)) * Convert(BIGINT, isnull(mt.Rate, 0))) AS ClaimedAmount
,GETDATE()
,GETDATE()
,#Updateby
,0
FROM #myTableType mt
JOIN #myInvoiceTable mt2 ON 1 = 1
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
END
And now which join 1=1 i am applying in Database it repeats the number of line and invoice quantity and all amount gets doubled And USELESS.

Suggestion needed writing a complex query - sqlite

I have 4 columns in a table called musics - 'artist','genre', 'writer' , 'producer'.
I need to write a query such that, it returns a value 0 , if there are no repetition of values corresponding to the column name; if there is a repetition of values, it should return a value 1, corresponding to that column name.
Any help is much appreciated
SELECT (COUNT(artist) <> COUNT(DISTINCT artist)) artist,
(COUNT(genre) <> COUNT(DISTINCT genre)) genre,
(COUNT(writer) <> COUNT(DISTINCT writer)) writer,
(COUNT(producer) <> COUNT(DISTINCT producer)) producer
FROM musics
Another version
SELECT
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY artist HAVING COUNT(*) > 1) a
) artist,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY genre HAVING COUNT(*) > 1) g
) genre,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY writer HAVING COUNT(*) > 1) w
) writer,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY producer HAVING COUNT(*) > 1) p
) producer
Sample data
| artist | genre | writer | producer |
------------------------------------------
| artist1 | genre1 | writer1 | producer1 |
| artist2 | genre2 | writer1 | producer2 |
| artist1 | genre3 | writer3 | producer3 |
Sample output:
| artist | genre | writer | producer |
--------------------------------------
| 1 | 0 | 1 | 0 |
SQLFiddle
For Artist
select convert(bit,(count(1)-1))
from table_name
group by artist -- <-- Replace artist with column name for which duplicate
write a select count statement using distinct with specified column and another select count without distinct and compare both of them based on your requirement
you can use 4 different query with union & each query must contain count(column name) + group by clause

Resources