SQL check is zero or null - tsql

Hi i have some problem
Need to check is null or zero by column if something wrong doing some algorithm
This is a table:
col1 col2 col3 col4
1 0 3376 0
2 600 null 14468.5714
3 null 0 0
4 600 3376 null
COALESCE doesn't work with zero "0" value, case its too huge
need to realize some of that
, CAST(COALESCE(col2, (col3/7), (col4/30)) as money) col2
, CAST(COALESCE(col3, (col2*7), (col4/30*7))as money) col3
, CAST(COALESCE(col4, (col3/7*30),(col2*30))as money) col4
how to solve this in fastest way. ThanX

While COALESCE allows you to replace a NULL with a specific value, NULLIF will allow you to replace a specific value with a NULL. You could use the latter on 0 and end up with something like this:
, CAST(
COALESCE(
NULLIF(col2, 0),
NULLIF(col3, 0) / 7,
NULLIF(col4, 0) / 30
) AS money
) AS col2
, CAST(
COALESCE(
NULLIF(col3, 0),
NULLIF(col2, 0) * 7,
NULLIF(col4, 0) / 30 * 7)
) AS money
) AS col3
, CAST(
COALESCE(
NULLIF(col4, 0),
NULLIF(col3, 0) / 7 * 30,
NULLIF(col2, 0) * 30
) AS money
) AS col4
Still rather long, if you asked me, but definitely shorter than using CASEs. The last NULLIF in each expression might be unnecessary, I left them there for consistency. Perhaps you could add a fourth argument of 0 everywhere, just to make sure the result is never a NULL.

Why not just use a CASE condition like
CASE WHEN col2 is not null and col2 <> 0 THEN your_calculation

you can do a subquery with CASE statement to check for zero and return NULL.
your current query can then be run on the subquery.
I see that using case would be ugly as you have 3 expressions in COALESCE
SELECT
CAST(COALESCE(col2 , (col3/7), (col4/30)) as money) col2
, CAST(COALESCE(col3, (col2*7), (col4/30*7))as money) col3
, CAST(COALESCE(col4, (col3/7*30),(col2*30))as money) col4
from
( SELECT case when col2 =0 then NULL else col2 end as col2,
case when col3 =0 then NULL else col3 end as col3,
case when col4 =0 then NULL else col4 end as col4
from Table1) T

Related

PostgreSQL Crosstab issues / "Return and SQL tuple descriptions are incompatible"

Good afternoon, I am using POSTGRESql version 9.2 and I'm trying to use a crosstab function to transpose two columns on a table so that i can later join it to a different SELECT query.
I have installed the tablefunc extension.
However i keep getting this "Return and SQL tuple descriptions are incompatible" error which seems to be because of typecasts.
I don't need them to be a specific type.
My original SELECT query is this
SELECT inventoryid, ttype, tamount
FROM inventorytesting
Which gives me the following result:
inventoryid ttype tamount
2451530088940460 7 0.2
2451530088940460 2 0.5
2451530088940460 8 0.1
2451530088940460 1 15.7
8751530077940461 7 0.7
8751530077940461 2 0.2
8751530077940461 8 1.1
8751530077940461 1 19.2
and my goal is to get it like:
inventoryid 7 2 8 1
8751530077940461 0.7 0.2 1.1 19.2
2451530088940460 0.2 0.5 0.1 15.7
The 'ttype' field has 49 different values such as "7","2","8","1" which are fixed.
The 'tamount' field varies its values depending on the 'inventoryid' field but there will always be 49 of them, even if its value is zero. It will never be "null".
I have tried a few variations that i could find in the internet which sum up to this:
SELECT *
FROM crosstab (
$$SELECT inventoryid, ttype, tamount
FROM inventorytesting
WHERE inventoryid = '2451530088940460'
ORDER BY inventoryid, ttype$$
)
AS ct("inventoryid" text,"ttype" smallint,"tamount" numeric)
The fieldtypes on the inventorytesting table are
select column_name, data_type from information_schema.columns
where table_name = 'inventorytesting'
Results:
column_name data_type
id bigint
ttype smallint
tamount numeric
tunit text
tlessthan smallint
plantid text
sessiontime bigint
deleted smallint
inventoryid text
docdata text
docname text
labid bigint
Any pointers would be great.
demo:db<>fiddle
The resulting table definition has to contain the table structure you are expecting - the pivoted one - and not the structure of the given one:
SELECT *
FROM crosstab(
$$SELECT inventoryid, ttype, tamount
FROM inventorytesting
WHERE inventoryid = '2451530088940460'
ORDER BY inventoryid, ttype$$
)
AS ct("inventoryid" text,"type1" numeric,"type2" numeric,"type7" numeric,"type8" numeric)
Addionally there is no need to use the crosstab function. You can achieve a pivot by simply using the standard CASE function:
SELECT
inventoryid,
SUM(CASE WHEN ttype = 1 THEN tamount END) AS type1,
SUM(CASE WHEN ttype = 2 THEN tamount END) AS type2,
SUM(CASE WHEN ttype = 7 THEN tamount END) AS type7,
SUM(CASE WHEN ttype = 8 THEN tamount END) AS type8
FROM
inventorytesting
GROUP BY 1
If you were on 9.4 or higher you could use the Postgres specific FILTER clause:
SELECT
inventoryid,
SUM(tamount) FILTER (WHERE ttype = 1) AS type1,
SUM(tamount) FILTER (WHERE ttype = 2) AS type2,
SUM(tamount) FILTER (WHERE ttype = 7) AS type7,
SUM(tamount) FILTER (WHERE ttype = 8) AS type8
FROM
inventorytesting
GROUP BY 1
demo:db<>fiddle
With the crosstab, you define the actual result table (basically the result of the pivot). The input query defines three columns which are then processed as:
grouping column result in the actual rows
the pivot columns
value for the pivot column
In your case, the crosstab therefore needs to be defined as:
ct(
"inventoryid" text,
"tamount_1" numeric,
"tamount_2" numeric,
"tamount_3" numeric,
...
)
The column header will then correlate to a certain value of column ttype in the order as defined by the inner query's ORDER BY.
The thing with crosstab is that missing values for ttype (e.g. some value returned for 4 but not for 3), the resulting columns would be 1, 2, 4, ... with 3 being missing. Here, you'd have to make sure (if you need consistent output) that your inner query returns at least a NULL row (e.g. via LEFT JOIN).

SQL Select where exclusive Or /Case

I need a query based on an exclusive Or statement for this I try using the case but I won't get a result in case of Null...
...
and b.[U_Periode] = CASE
when (b.U_Periode= #period) then #period
when (b.U_Periode is NULL ) then null
end
...
The Case that won't be catched is... if B.U_Status is null and b.U_Periode is null.
If the var Periode match the Value and the U_Status = 0 or 1
the only way getting this working for me was this:
...
and
ISNULL( b.[U_Status],'0') = CASE
when (b.U_Status= '1') then '1'
when (isnull( b.U_Status,'0')= '0') then '0'
end
and
ISNULL (b.[U_Periode],'01.01.1901') = CASE
when (b.U_Periode= #period) then #period
when (ISNULL (b.U_Periode,'01.01.1901') = '01.01.1901' ) then '01.01.1901'
end
are there any other better solutions for this?
Best regards
Oliver
Okay... here is my Problem
Table1
InsID ContractID
1 1
2 1
Table2
ID insid Period Status Count
1 1 null null 100
2 1 30.09.2015 1 500
3 2 null null 100
4 2 30.09.2015 1 500
Case '31.08.2015'
in total Value should be 200
in case of '30.09.2015'
the Value should be 1.000
XOR /OR will do the same in this case.
Value case '31.08.2015' = 200
value Case ' 30.09.2015 = 2200
So this is somesing like a subquery
left join (
[dbo].[Table2]b
inner join [dbo].[Table 3]K on k.DocEntry = b.DocEntry and CAST( k.U_CSetID as int) >0
)
on b.[U_InsID] in(select... but here I should have an if statement...
If there are results matching the Date join this
If not than join the result NULL is matching to periode...
Okay.. here is the complete query
the Table2 with the Date of 31.08.2015 should have one Record that include
B_STATUS = Null and B.Preiode = null and there is no available record with the U_Periode '31.08.2015' and a Staus ...
with a date of 30.09.2015
there is a Record matching U_Period = '30.09.2015' in this case the Record with U_Period=null should not effect the result...
Declare #period as varchar(20)= '31-08-2015 00:00:00'
declare #Customer as Varchar(15)='12345'
declare #Contract as varchar(30) = '123'
declare #test as varchar(1) = null
select SUM(cast(K.U_Count as decimal))as counter, K.U_CounterTyp
from [dbo].[Table1] a
left join (
[dbo].[Table2]b
inner join [dbo].[Table 3]K on k.DocEntry = b.DocEntry and CAST( k.U_CSetID as int) >0
)
on b.[U_InsID]=a.[insID] and b.[U_ObjectType]in ('5','1') and
ISNULL( b.[U_Status],'0') = CASE
when (b.U_Status= '1') then '1'
when (isnull( b.U_Status,'0')= '0') then '0'
end
and
ISNULL (b.[U_Periode],'01.01.1901') = CASE
when (b.U_Periode= #period) then #period
when (ISNULL (b.U_Periode,'01.01.1901') = '01.01.1901' ) then '01.01.1901'
end
where a.[customer] =#Customer and a.[Status]='A' and a.[U_ContrCount]='1'
and a.[manufSN] in(
select c.[ManufSN] from [dbo].[Table4] c
inner join [dbo].[OCTR]d on d.[ContractID] = c.[ContractID]
where c.[ManufSN]=a.[manufSN]
and d.[CstmrCode] = a.[customer] and d.[ContractID]=#Contract
)
group by K.U_CounterTyp
you must use the "^" operand, this is XOR operand in TSQL
expression ^ expression
the expression must return or 0 or 1...
stupid example:
WHERE (name like "stackoverflow") ^ (age > 10)
font: https://msdn.microsoft.com/en-us/library/ms190277(v=sql.105).aspx
Update for your check
WHERE (CONVERT(VARCHAR(10), a.[U_Periode], 104) = '30.08.2015') != (a.U_Periode IS NULL)
Olay here is my funktion that chek either a Date Result is there or not.
The only thing is, that the performance is not the best if I run hundreths of sentences ... foreach record (up to app. 1000) I need to query each subtable... and there are many many records in...
Anyway this is the function
CREATE FUNCTION fn_GetInsCounters(#InsId as Varchar(30), #Date as datetime)
returns datetime
as
begin
declare #count as int = 0
declare #Retruns as Datetime
set #count =
(
select count( b.Docentry)
from [dbo].[Table2]b
where b.U_Insid =#InsID
and b.U_Status <> '2'
and b.[U_ObjectType]in ('5','1')
and b.U_Periode=#Date
)
if(#count>0)
begin
set #Retruns = #date
end
else
begin
set #Retruns = '01.01.1901'
end
return #Retruns
end
if anyone gets a better idea???
Best regards
Oliver

Split string with a separator and set each part in variables in TSQL

I need to split some strings with this format:
V_9_0_2_2_70_0_0_3_B
The separator is '_'.
I want that each value is stored in my variables
DECLARE #w_grup1 char(1),
#w_grup2 char(1),
#w_grup3 varchar(10),
#w_grup4 char(1),
#w_grup5 char(1),
#w_grup6 varchar(10),
#w_grup7 char(1),
#w_grup8 varchar(10),
#w_grup9 char(1),
#w_grup10 char(1)
How do I do that? Some suggestion?
I figured your best bet is a recursive CTE. Note: I didn't load the data into a table but I figure you can do that easily enough from my results. If you need anything else, let me know.
DECLARE #YourTable table (ID INT IDENTITY(1,1), String varchar(200))
INSERT #YourTable(String)
VALUES ('V_9_0_2_2_70_0_0_3_B'),
('ABC_01_23_45_67_89_10_11_12_XYZ');
WITH SplitString AS
(
SELECT ID,
LEFT(String,CHARINDEX('_',String)-1) AS Part,
RIGHT(String,LEN(String)-CHARINDEX('_',String)) AS Remainder,
1 AS RecursionCount
FROM #YourTable
WHERE String IS NOT NULL AND CHARINDEX('_',String) > 0
UNION ALL
SELECT ID,
LEFT(Remainder,CHARINDEX('_',Remainder)-1),
RIGHT(Remainder,LEN(Remainder)-CHARINDEX('_',Remainder)),
RecursionCount + 1
FROM SplitString
WHERE Remainder IS NOT NULL AND CHARINDEX('_',Remainder) > 0
UNION ALL
SELECT ID,
Remainder,
null,
recursionCount + 1
FROM SplitString
WHERE Remainder IS NOT NULL AND CHARINDEX('_',Remainder) = 0
)
SELECT *
FROM
(
SELECT CONCAT('w_grup',RecursionCount) w_grups,Part,ID
FROM SplitString
) A
PIVOT
(
MAX(Part) FOR w_grups IN (w_grup1,w_grup2,w_grup3,w_grup4,w_grup5,w_grup6,w_grup7,w_grup8,w_grup9,w_grup10)
) pvt
Results:
ID w_grup1 w_grup2 w_grup3 w_grup4 w_grup5 w_grup6 w_grup7 w_grup8 w_grup9 w_grup10
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 V 9 0 2 2 70 0 0 3 B
2 ABC 01 23 45 67 89 10 11 12 XYZ
So, something like this?
with split as
(
select
item = cast('' as varchar(max)),
source = cast('V_9_0_2_2_70_0_0_3_B' as varchar(max))
union all
select
item = substring(source, 1, charindex('_', source)),
source = substring(source, charindex('_', source) + 1, 10000)
from split
where source > ''
)
select substring(item, 1, charindex('_', item) -1)
from split
where item > ''
from this question

Validating Excel using XML and moving to SQL Server destination

Is there a built in function (as opposed to a UDF) or can someone provide sample code to split a String to two columns when a character is encountered?
Sample:
1234:abcd
split the above string into 1234 and abcd into two columns
Have a go with this. Its not pretty but it produces the two columns (assuming : is always the divider):
declare #test varchar(20)
set #test = '1234:abcd'
select
leftcol = left(#test,charindex(':',#test)-1),
rightcol = right(#test,len(#test) - charindex(':',#test))
In otherwords, its not a build in function, but it is inline sql code.
Title/tag mismatch?
For Excel, if A1 contains the value:
make B1 =LEFT(A1,IF(ISERROR(FIND(":",A1)),LEN(A1),FIND(":",A1)-1))
make C1 =RIGHT(A1,IF(ISERROR(FIND(":",A1)),0,LEN(A1)-FIND(":",A1)))
Or for T-SQL + a string variable;
DECLARE #F VARCHAR(64) = '1234:ABCD'
IF #F LIKE '%:%'
SELECT SUBSTRING(#F, 1, CHARINDEX(':', #F, 1) - 1) AS COL1,
SUBSTRING(#F, CHARINDEX(':', #F, 1) + 1, LEN(#F)) AS COL2
ELSE
SELECT #F AS COL1, NULL AS COL2
for a select;
;WITH faketable (fld) AS (
SELECT 'aaa:123' as fld
UNION SELECT 'ddddd'
)
SELECT
CASE WHEN fld LIKE '%:%' THEN SUBSTRING(fld, 1, CHARINDEX(':', fld, 1) - 1) ELSE fld END AS COL1 ,
CASE WHEN fld LIKE '%:%' THEN SUBSTRING(fld, CHARINDEX(':', fld, 1) + 1, LEN(fld)) ELSE NULL END AS COL2
FROM faketable
>COL1 COL2
>aaa 123
>ddddd NULL

SQL Running Subtraction

Just a brief of business scenario is table has been created for a good receipt. So here we have good expected line with PurchaseOrder(PO) in first few line. And then we receive each expected line physically and that time these quantity may be different, due to business case like quantity may damage and short quantity like that. So we maintain a status for that eg: OK, Damage, also we have to calculate short quantity based on total of expected quantity of each item and total of received line.
if object_id('DEV..Temp','U') is not null
drop table Temp
CREATE TABLE Temp
(
ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Item VARCHAR(32),
PO VARCHAR(32) NULL,
ExpectedQty INT NULL,
ReceivedQty INT NULL,
[STATUS] VARCHAR(32) NULL,
BoxName VARCHAR(32) NULL
)
Please see first few line with PO data will be the expected lines,
and then rest line will be received line
INSERT INTO TEMP (Item,PO,ExpectedQty,ReceivedQty,[STATUS],BoxName)
SELECT 'ITEM01','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01','PO-02','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM02','PO-01','40',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-01','50',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-02','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-03','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM04','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01',NULL,NULL,'20','OK','box01' UNION ALL
SELECT 'ITEM01',NULL,NULL,'25','OK','box02' UNION ALL
SELECT 'ITEM01',NULL,NULL,'5','DAMAGE','box03' UNION ALL
SELECT 'ITEM02',NULL,NULL,'38','OK','box04' UNION ALL
SELECT 'ITEM02',NULL,NULL,'2','DAMAGE','box05' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box06' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box07' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box08' UNION ALL
SELECT 'ITEM03',NULL,NULL,'10','DAMAGE','box09' UNION ALL
SELECT 'ITEM04',NULL,NULL,'25','OK','box10'
Below Table is my expected result based on above data.
I need to show those data following way.
So I appreciate if you can give me an appropriate query for it.
Note: first row is blank and it is actually my table header. :)
SELECT '' as 'ITEM', '' as 'PO#', '' as 'ExpectedQty',
'' as 'ReceivedQty','' as 'DamageQty' ,'' as 'ShortQty' UNION ALL
SELECT 'ITEM01','PO-01','30','30','0' ,'0' UNION ALL
SELECT 'ITEM01','PO-02','20','15','5' ,'0' UNION ALL
SELECT 'ITEM02','PO-01','40','38','2' ,'0' UNION ALL
SELECT 'ITEM03','PO-01','50','50','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-02','30','30','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-03','20','10','10','0' UNION ALL
SELECT 'ITEM04','PO-01','30','25','0' ,'5'
Note : we don't received more than expected.
solution should be based on SQL 2000
You should reconsider how you store this data. Separate Expected and Received+Damaged in different tables (you have many unused (null) cells). This way any query should become more readable.
I think what you try to do can be achieved more easily with a stored procedure.
Anyway, try this query:
SELECT Item, PO, ExpectedQty,
CASE WHEN [rec-consumed] > 0 THEN ExpectedQty
ELSE CASE WHEN [rec-consumed] + ExpectedQty > 0
THEN [rec-consumed] + ExpectedQty
ELSE 0
END
END ReceivedQty,
CASE WHEN [rec-consumed] < 0
THEN CASE WHEN DamageQty >= -1*[rec-consumed]
THEN -1*[rec-consumed]
ELSE DamageQty
END
ELSE 0
END DamageQty,
CASE WHEN [rec_damage-consumed] < 0
THEN DamageQty - [rec-consumed]
ELSE 0
END ShortQty
FROM (
select t1.Item,
t1.PO,
t1.ExpectedQty,
st.sum_ReceivedQty_OK
- (sum(COALESCE(t2.ExpectedQty,0))
+t1.ExpectedQty)
[rec-consumed],
st.sum_ReceivedQty_OK + st.sum_ReceivedQty_DAMAGE
- (sum(COALESCE(t2.ExpectedQty,0))
+t1.ExpectedQty)
[rec_damage-consumed],
st.sum_ReceivedQty_DAMAGE DamageQty
from #tt t1
left join #tt t2 on t1.Item = t2.Item
and t1.PO > t2.PO
and t2.PO is not null
join (select Item
, sum(CASE WHEN status = 'OK' THEN ReceivedQty ELSE 0 END)
sum_ReceivedQty_OK
, sum(CASE WHEN status != 'OK' THEN ReceivedQty ELSE 0 END)
sum_ReceivedQty_DAMAGE
from #tt where PO is null
group by Item) st on t1.Item = st.Item
where t1.PO is not null
group by t1.Item, t1.PO, t1.ExpectedQty,
st.sum_ReceivedQty_OK,
st.sum_ReceivedQty_DAMAGE
) a
order by Item, PO