Check Missing data when id is not null or blank - sql

I am trying to check if data is present for ID but not for its corresponding value then entire row discard from output. If both value and id are blank then its ok, and those row retain in the output
DECLARE #TAB TABLE
(
ID VARCHAR (50),
SKU VARCHAR (50),
Resistor_ID1 VARCHAR (50),
Resistor_Value VARCHAR (50),
Capacitor_ID VARCHAR (50),
Capacitor_Value VARCHAR (50),
Inductor_ID VARCHAR (50),
Inductor_Value VARCHAR (50)
)
INSERT #TAB
SELECT '1', 'BPN1256', '1033', '' , 'RMA56', 'Ceramic', 'PVAN59', 'Ferrite' UNION ALL
SELECT '1', 'SAN9286', '' , '' , 'TMA56', 'FILM' , '' , '' UNION ALL
SELECT '1', 'RJA1896', '3033', '35OHM', 'UMA56', 'Ceramic', 'PVAN59', 'Ferrite' UNION ALL
SELECT '1', 'DNN5256', '4033', '45OHM', 'QMA56', '' , 'PVAN59', 'Ferrite' UNION ALL
SELECT '1', 'LXA6556', '5033', '65OHM', 'ZMA56', 'FILM' , 'PVAN59', ''
Expected Output
1 SAN9286 TMA56 FILM
1 RJA1896 3033 35OHM UMA56 Ceramic PVAN59 Ferrite
Please share your expertise.
Thanks

DECLARE #TAB TABLE
(
ID VARCHAR (50),
SKU VARCHAR (50),
Resistor_ID1 VARCHAR (50),
Resistor_Value VARCHAR (50),
Capacitor_ID VARCHAR (50),
Capacitor_Value VARCHAR (50),
Inductor_ID VARCHAR (50),
Inductor_Value VARCHAR (50)
)
INSERT #TAB
SELECT '1', 'BPN1256', '1033', '','RMA56', 'Ceramic', 'PVAN59', 'Ferrite' UNION ALL
SELECT '1', 'SAN9286', '', '', 'TMA56', 'FILM', '', '' UNION ALL
SELECT '1', 'RJA1896', '3033', '35OHM', 'UMA56', 'Ceramic', 'PVAN59', 'Ferrite' UNION ALL
SELECT '1', 'DNN5256', '4033', '45OHM', 'QMA56', '', 'PVAN59', 'Ferrite' UNION ALL
SELECT '1', 'LXA6556', '5033', '65OHM', 'ZMA56', 'FILM', 'PVAN59', ''
SELECT * FROM #TAB t WHERE ((t.Resistor_ID1<>'' AND t.Resistor_Value<>'') OR (t.Resistor_ID1='' AND t.Resistor_Value=''))
AND ((t.Capacitor_ID<>'' AND t.Capacitor_Value<>'') OR (t.Capacitor_ID='' AND t.Capacitor_Value=''))
AND ((t.Inductor_ID<>'' AND t.Inductor_Value<>'') OR (t.Inductor_ID='' AND t.Inductor_Value=''))
But you should take care on using empty string or null. There are significant differences. In your example you have used empty string to show absence of data and the select above is applicable for empty string.

Well, why not just do it directly? You need rows when both ID and value are blank, or when both ID and value are not blank. Put this into where clause, repeat for 3 id-value pairs, and you're done.
SELECT * FROM #TAB
WHERE (Resistor_ID1 != '' and Resistor_Value != '' or Resistor_ID1 = '' and Resistor_Value = '')
and (Capacitor_ID != '' and Capacitor_Value != '' or Capacitor_ID = '' and Capacitor_Value = '')
and (Inductor_ID != '' and Inductor_Value != '' or Inductor_ID = '' and Inductor_Value = '')
Important note: you didn't specify your DBMS, so you might need to alter that query syntax. For example, in Oracle a blank string('') is treated as null value and should be checked with value is null instead of value = ''. In Mysql, null and empty string are different things, and should be checked differently.
UPD: it should work fine using = and != for MS SQL server, if you actually inserted blank strings and not nulls. If you're not sure, you might want to take a look at How do I check if a Sql server string is null or empty

Related

SQL Server pivot values with different data types

i am trying to pivot all values in different type in MSSQL 2016. I could not find a way how i can pivot different data types..
The first table are initial form / structure. The second table is the desired shape.
I was trying the following SQL code to pivot my values
SELECT
[id] AS [id],
FIRSTNAME,
LASTNAME,
BIRTHDATE,
ADDRESS,
FLAG,
NUMBER
FROM (
SELECT
[cm].[key] AS [id],
[cm].[column] AS [column],
[cm].[string] AS [string],
[cm].[bit] AS [bit],
[cm].[xml] AS [xml],
[cm].[number] AS [number],
[cm].[date] AS [date]
FROM [cmaster] AS [cm]
) AS [t]
PIVOT (
MAX([string]) --!?!?
FOR [column] IN (
FIRSTNAME,
LASTNAME,
BIRTHDATE,
ADDRESS,
FLAG,
NUMBER
)
) AS [p]
I think your best bet is to use conditional aggregation, e.g.
SELECT cm.id,
FIRSTNAME = MAX(CASE WHEN cm.[property] = 'firstname' THEN cm.[string] END),
LASTNAME = MAX(CASE WHEN cm.[property] = 'lastname' THEN cm.[string] END),
BIRTHDATE = MAX(CASE WHEN cm.[property] = 'birthddate' THEN cm.[date] END),
FLAG = CONVERT(BIT, MAX(CASE WHEN cm.[bit] = 'flag' THEN CONVERT(TINYINT, cm.[boolean]) END)),
NUMBER = MAX(CASE WHEN cm.[property] = 'number' THEN cm.[integer] END)
FROM cmaster AS cm
GROUP BY cm.id;
Although, as you can see, your query becomes very tightly coupled to your EAV model, and why EAV is considered an SQL antipattern. Your alternative is to create a single column in your subquery and pivot on that, but you have to convert to a single data type, and lose a bit of type safety:
SELECT id, FIRSTNAME, LASTNAME, BIRTHDATE, ADDRESS, FLAG, NUMBER
FROM (
SELECT id = cm.[key],
[column] = cm.[column],
Value = CASE cm.type
WHEN 'NVARCHAR' THEN cm.string
WHEN 'DATETIME' THEN CONVERT(NVARCHAR(MAX), cm.date, 112)
WHEN 'XML' THEN CONVERT(NVARCHAR(MAX), cm.xml)
WHEN 'BIT' THEN CONVERT(NVARCHAR(MAX), cm.boolean)
WHEN 'INT' THEN CONVERT(NVARCHAR(MAX), cm.integer)
END
FROM cmaster AS cm
) AS t
PIVOT
(
MAX(Value)
FOR [column] IN (FIRSTNAME, LASTNAME, BIRTHDATE, ADDRESS, FLAG, NUMBER)
) AS p;
In order to make the result as per your request, first thing is we need to bring the data in to one format which is compatible with all data types. VARCHAR is ideal for that. Then prepare the base table using a simple select query, then PIVOT the result.
In the last projection, if you want, you can convert the data back in to the original format.
This query can be written dynamically as well to obtain the result as records are added. Here I provide the static answer according to your data. If you need a more generic dynamic answer, let me know. So I can post here.
--data insert scripts I used:
CREATE TABLE First_Table
(
[id] int,
[column] VARCHAR(10),
[string] VARCHAR(20),
[bit] BIT,
[xml] [xml],
[number] INT,
[date] DATE
)
SELECT GETDATE()
INSERT INTO First_Table VALUES(1, 'FIRST NAME', 'JOHN' , NULL, NULL, NULL, NULL)
INSERT INTO First_Table VALUES(1, 'LAST NAME', 'DOE' , NULL, NULL, NULL, NULL)
INSERT INTO First_Table VALUES(1, 'BIRTH DATE', NULL , NULL, NULL, NULL, '1985-02-25')
INSERT INTO First_Table VALUES(1, 'ADDRESS', NULL , NULL, 'SDFJDGJOKGDGKPDGKPDKGPDKGGKGKG', NULL, NULL)
INSERT INTO First_Table VALUES(1, 'FLAG', NULL , 1, NULL, NULL, NULL)
INSERT INTO First_Table VALUES(1, 'NUMBER', NULL , NULL, NULL, 20, NULL)
SELECT
PIVOTED.* FROM
(
--MAKING THE BASE TABLE FOR PIVOT
SELECT
[id]
,[column] AS [COLUMN]
, CASE WHEN [column] = 'FIRST NAME' then [string]
WHEN [column] = 'LAST NAME' then [string]
WHEN [column] = 'BIRTH DATE' then CAST([date] AS VARCHAR(100))
WHEN [column] = 'ADDRESS' then CAst([xml] as VARCHAR(100))
WHEN [column] = 'FLAG' then CAST([bit] AS VARCHAR(100))
else CAST([number] AS VARCHAR(100)) END AS [VALUE]
FROM First_Table
) AS [P]
PIVOT
(
MIN ([P].[VALUE])
FOR [column] in ([FIRST NAME],[LAST NAME],[BIRTH DATE],[ADDRESS],[FLAG],[NUMBER])
) AS PIVOTED
RESULT:
SQL:
SELECT
            ID,
            FIRSTNAME,
            ...,
            FLAG = CAST (FLAG AS INT),
            ...
FROM
            (
            SELECT
                        *
            FROM
                        (
                        SELECT
                                    f.ID,
                                    f.PROPERTY,
                                    f.STRING + f."INTEGER" + f.DATETIME + f.BOLLEAN + f.XML AS COLS
                        FROM
                                    FIRSTTBL f)
            PIVOT(
                        min(COLS) FOR PROPERTY IN
                                    (
                                    'firstname' AS firstname,
                                    'lastname' AS lastname,
                                    'birthdate' AS birthdate,
                                    'address' AS address,
                                    'flag' AS flag,
                                    'number' AS "NUMBER"
                                    )
                        )
            )
According to the original table, there is one and only one non-null value among STRING, INTEGER, DATETIME, BOLLEAN and XML columns for any row, so we just need to get the first non-null value and assign it to the corresponding new column. It is not difficult to perform the transposition using PIVOT function, except that we need to handle different data types according to the SQL rule, which requires that each column have a consistent type. For this task, first we need to convert the combined column values into a string, perform row-to-column transposition, and then convert string back to the proper types. When there are a lot of columns, the SQL statement can be tricky, and dynamic requirements are even hard to achieve.
Yet it is easy to write the code using the open-source esProc SPL:
 
A
1
=connect("MSSQL")
2
=A1.query#x("SELECT * FROM FIRSTTBL")
3
=A2.pivot(ID;PROPERTY,~.array().m(4:).ifn();"firstname":"FIRSTNAME", "lastname":"LASTANME","birthdate":"BIRTHDAY","address":"ADDRESS","flag":"FLAG","number":"NUMBER")
SPL does not require that data in the same column have consistent type. It is easy for it to maintain the original data types while performing the transposition.

T-SQL cast and converting issues from source tables to destination

I have a table as follows:
create table dbo.##Table_A00
(
RowNo int,
TRANSACTION_TYPE varchar(3),
ORGANISATION_ID numeric (10),
FILE_TYPE varchar(3),
CREATION_DATE datetime,
CREATION_TIME varchar(3),
GENERATION_NUMBER numeric (6)
)
However the source files I am using is a table used to capture flat files and they can be in any data format.
What I have in the source table the data type looks like this:
CREATE TABLE ##Table_Alltextfiles
(
rowID int identity (1,1),
[Col1] varchar(50),
[Col2] varchar(250),
[Col3] varchar(50),
[Col4] varchar(50),
[Col5] varchar(50),
[Col6] varchar(50),
[Col7] varchar(50)
)
What I want to do is insert into ##Table_A00 (destination) all rows from ##Table_Alltextfiles (source)
However I am having issues doing this as the data type are mis match and I have tried casting it without success.
What can I do to get the value of varchar to its appropiate destination i.e if its a date field or if its a numeric.
This is what I have been tring to do:
insert into dbo.##Table_A00
select
rowID,
col1, cast(col2 as numeric (10)),
col3, cast(col4 as date),
col5, cast(col6 as numeric (6))
from
##Table_Alltextfiles
where
col1 = 'A00'
Thank you
Try out with the below query.
insert into dbo.##Table_A00
select
rowID,
SUBSTRING(col1,1,3), case when ISNUMERIC(col2)=1 THEN cast(col2 as numeric (10)) ELSE NULL END,
SUBSTRING(col3,1,3), case when ISDATE(col4)=1 THEN cast(col4 as datetime) ELSE NULL END,
SUBSTRING(col5,1,3), case when ISNUMERIC(col6)=1 THEN cast(col6 as numeric (6)) ELSE NULL END
from
##Table_Alltextfiles
where
col1 = 'A00'

check valid value exist after first semicolon

may anyone please help me if possible
I am trying to check whether data in NONVALID column of #APPLICATION table contain remarks_1 value
which is present in #ATTRIBUTE table
we can join the table with SKU as key column
Table
DECLARE #ATTRIBUTE TABLE
(
SKU VARCHAR (50),
REMARKS_1 VARCHAR (50)
)
INSERT #ATTRIBUTE
SELECT 'AMRN205-740','P-215-40-18;' UNION ALL
SELECT 'NATN205-740','LT-275-65-20' UNION ALL
SELECT 'NATN205-740','XX-275-65-20'
DECLARE #APPLICATION TABLE
(
SKU VARCHAR (50),
NONVALID VARCHAR (50)
)
INSERT #APPLICATION
SELECT 'AMRN205-740','Plus Size; P-215-40-18; 87H' UNION ALL
SELECT 'BCTN205-740','Plus Size; P-215-40-18; 86H' UNION ALL
SELECT 'NATN205-740','Plus Size; 86H' UNION ALL
SELECT 'AMRN205-740', NULL union all
SELECT 'BCTN205-740','P-215-40-18; 86H' UNION ALL
SELECT ''BCTN205-740', ;86H'
EXPECTED OUTPUT
'NATN205-740','Plus Size; 86H'
'BCTN205-740','P-215-40-18; 86H'-- plus size or semi-plus size before first semicolon in a string is missing
BCTN205-740', ;86H'-- plus size or semi-plus size before first semicolon in a string
AS AFTER FIRST SEMICOLON it does not contain either LT-275-65-20 or XX-275-65-20
Thanks a lot
I advice you to normalize better your tables:
I like prefer a table for SKU list named SKU as follow:
CREATE TABLE sku
(id int, name varchar(100))
id is a PK, name has the content of your field SKU (of your #attribute table)
CREATE TABLE attribute
(id int,
fk_sku int,
name varchar(100))
where you have attribute linked SKU
And finally,
CREATE TABLE application
(id int,
fk_sku int,
fk_attribute int) <-- if you want to store this info or another I don't know
Try this:
SELECT *
FROM #APPLICATION app
WHERE NOT EXISTS(
SELECT 'ATTRIBUTE'
FROM #ATTRIBUTE att
WHERE att.SKU = app.SKU
AND PATINDEX('%' + att.REMARKS_1 + '%', app.NONVALID) > 0
)
and app.NONVALID IS NOT NULL
This query returns 'NATN205-740','Plus Size; 86H' and 'BCTN205-740','Plus Size; P-215-40-18; 86H'
If you want only 'NATN205-740','Plus Size; 86H' so:
SELECT *
FROM #APPLICATION app
WHERE EXISTS(
SELECT 'ATTRIBUTE'
FROM #ATTRIBUTE att
WHERE att.SKU = app.SKU
)
AND NOT EXISTS(
SELECT 'ATTRIBUTE'
FROM #ATTRIBUTE att
WHERE att.SKU = app.SKU
AND PATINDEX('%' + att.REMARKS_1 + '%', app.NONVALID) > 0
)
and app.NONVALID IS NOT NULL

Using SQL CAST with the casting type retrieved from the database

Just wondering if this is even possible, and if so... how?
Table (Foos):
Column Value
Id int pk autoinc
Type varchar(50) not null
Name varchar(50) not null
Value varchar(50)
When I do a SELECT * FROM Foos I get the following:
Id Type Name Value
-------------------------------------------------
1 int MaxScore '100'
2 varchar(50) Greeting 'hello world'
3 datetime FollowupDate '01-01-2012'
I want to be able to create a SP that returns a record (using Id) and casting the record to the appropriate type before returning it. I know that I can easily just return the record as is and use an application to cast the string into the appropriate type... but I guess this is just to know if this is possible to expand knowledge and explore... something along the lines of:
SELECT Id, Name, CAST(Value AS [Type])
FROM Foos
But, I get the error:
Msg 243, Level 16, State 1, Line 1
Type Type is not a defined system type.
Am sure you could do this using dynamic SQL, but I don't expect it would be particularly elegant. Have you looked into storing values as sql_variant?
Something like this might give you what you deserve:
declare #Samples as Table ( Id Int Identity, Type VarChar(32), Name VarChar(32), Value VarChar(32) )
insert into #Samples ( Type, Name, Value ) values
( 'int', 'MaxScore', '100' ),
( 'varchar(50)', 'Greeting', 'hello world' ),
( 'datetime', 'FollowupDate', '01-01-2012' ),
( 'money', 'PriceEach', '99.4' )
select Id, Name, Type, Value, case
when Type = 'DateTime' then Cast( Convert( DateTime, Value, 104 ) as SQL_Variant )
when Type = 'Int' then Cast( Cast( Value as Int ) as SQL_Variant )
when Type = 'Money' then Cast( Cast( Value as Money ) as SQL_Variant )
when Type like 'VarChar(%)' then Cast( Cast( Value as VarChar(8000) ) as SQL_Variant )
else NULL end Variant
from #Samples
The VarChar case should have additional code to parse the length and use SUBSTRING() to return a suitable length.
May dog have mercy on your soul.

TSQL CASE on Multiple columns

declare #T table
(
ID int identity primary key,
FBK_ID BIGINT null,
TWT_ID BIGINT null,
LNK_ID NVARCHAR(50) null
);
Each record can ONLY have either a FBK_ID or a TWT_ID or a LNK_ID. No records have multiple values on those fields.
So mainly some records will have FacebookID values, some others have TwitterID, some others have LinkedInID.
QUESTIONS:
what is the fastest and cleanest way to do this?
Select ID, Type from #T
....where Type is a nvarchar(10) equal to either 'Facebook' or 'Twitter' or 'LinkedIn' depending on who has a value?
You could do something like this:
select
ID ,
case when FBK_ID is not null then FBK_ID
when TWT_ID is not null then TWT_ID
else LNK_ID end as LinkID
from #t
where <rest of your conditions if any>
You will get back the ID and one of the link IDS for the specific social network. If you want to know, additionally, to what kind of social network does the LinkID returned belongs to, you can add an extra column as so:
select
ID ,
case when FBK_ID is not null then FBK_ID,
when TWT_ID is not null then TWT_ID
else LNK_ID end as LinkID,
case when FBK_ID is not null then 'F'
when TWT_ID is not null then 'T'
else 'L' end as LinkIDFrom
from #t
where <rest of your conditions if any>
Each record can ONLY have either a FBK_ID or a TWT_ID or a LNK_ID. No
records have multiple values on those fields.
First fix your table:
DECLARE #T TABLE
(
ID INT IDENTITY PRIMARY KEY,
FBK_ID BIGINT NULL,
TWT_ID BIGINT NULL,
LNK_ID NVARCHAR(50) NULL,
CHECK (
(FBK_ID IS NOT NULL AND TWT_ID IS NULL AND LNK_ID IS NULL)
OR (FBK_ID IS NULL AND TWT_ID IS NOT NULL AND LNK_ID IS NULL)
OR (FBK_ID IS NULL AND TWT_ID IS NULL AND LNK_ID IS NOT NULL)
)
);
what is the fastest and cleanest way to do this?
This was quite fast for me to write, employing copy+paste, and looks clean to my eye:
SELECT ID, CAST('Facebook' AS NVARCHAR(10)) AS Type
FROM #T
WHERE FBK_ID IS NOT NULL
UNION
SELECT ID, CAST('Twitter' AS NVARCHAR(10)) AS Type
FROM #T
WHERE TWT_ID IS NOT NULL
UNION
SELECT ID, CAST('LinkedIn' AS NVARCHAR(10)) AS Type
FROM #T
WHERE LNK_ID IS NOT NULL;