SQL Server 2005 fill pivot table with 0s - tsql

I have this query in SQL Server 2005 (I used the more convenient 2008 insert method for example only) and I need the (null) to be replaced with 0 in the grid output.
Anyway to do this?

You would use the ISNULL() function. See SQL Fiddle
SELECT 'lessonid response ->'
, isnull([0], 0) as [0]
, isnull([1], 0) as [1]
, isnull([2], 0) as [2]
, isnull([3], 0) as [3]
, isnull([4], 0) as [4]
FROM (
SELECT lessonid AS 'lessonid response ->'
,ISNULL(response,0) as response
,count(response) AS respcnt
FROM tblRChoices
GROUP BY lessonid
,response
) TableResponse
PIVOT(SUM(respcnt) FOR response IN (
[0]
,[1]
,[2]
,[3]
,[4]
)) ResponsePivot

Related

Converting function instr from Oracle to PostgreSQL (sql only)

I am working on converting something from Oracle to PostgreSQL. In the Oracle file there is a function:
instr(string,substring,starting point,nth location)
Example:
instr(text, '$', 1, 3)
In PostgreSQL this does not exist, so I looked up an equivalent function (4 parameter is important).
I found:
The function strpos(str, sub) in Postgres is equivalent of instr(str, sub) in Oracle. Tried options via split_part (it didn't work out).
I need the same result only with standard functions Postgres (not own function).
Maybe someone will offer options, even redundant in code.
This may be done in pure SQL using string_to_array.
with tab(val) as (
select 'qwe$rty$123$456$78'
union all
select 'qwe$rty$123$'
union all
select '123$456$'
union all
select '123$456'
)
select
val
/*Oracle's signature: instr(string , substring [, position [, occurrence ] ])*/
, case
when
array_length(
string_to_array(substr(val /*string*/, 1 /*position*/), '$' /*substring*/),
1
) <= 3 /*occurrence*/
then 0
else
length(array_to_string((
string_to_array(substr(val /*string*/, 1 /*position*/), '$' /*substring*/)
)[:3/*occurrence*/],
'$'/*substring*/)
) + 1
end as instr
from tab
val
instr
qwe$rty$123$456$78
12
qwe$rty$123$
12
123$456$
0
123$456
0
Postgres: fiddle
Oracle: fiddle

How to raiserror in SQL Server 2016 compatible with SQL Server 2008

I wrote a script which check referencing objects on SQL Server versions from 2008 to 2016.
In SQL Server 2008, RaiseError works fine, but in SQL Server 2016, when I use TRY-CATCH, I don't see any errors in messages even they occur.
How to do it, to work also on 2016 version and be compatible up to the SQL Server 2008 version?
BEGIN TRY
INSERT INTO #refTable
SELECT
#nestLevel,
#referencingEntityFullName AS referencing_object_name,
OBJECT_ID(#referencingEntityFullName) AS referencing_object_id,
r.referencing_minor_id, --if > 0 then computed column
r.referenced_server_name,
r.referenced_database_name,
r.referenced_schema_name,
r.referenced_entity_name,
r.referenced_minor_name,
r.is_caller_dependent, --warning on these!
r.is_ambiguous--,
--r.is_selected,
--r.is_updated,
--r.is_select_all,
--r.is_insert_all,
--r.is_all_columns_found
FROM
sys.dm_sql_referenced_entities(#referencingEntityFullName, 'OBJECT') r
WHERE
r.referenced_entity_name = #currentReferencedEntityName
AND (#filterColumns = 0 OR
r.referenced_minor_name IN (SELECT ColumnName
FROM #piiTablesAndColumns ptac
WHERE ptac.TableName = #currentReferencedEntityFullName)
OR r.referenced_minor_name IS NULL --for SELECT *; check if it really works this way
)
END TRY
BEGIN CATCH
print('catched!' + #referencingEntityFullName + CAST(OBJECT_ID(#referencingEntityFullName) AS NVARCHAR(1000)))
INSERT INTO #refTable (referencing_object_name, referencing_object_id, referenced_entity_name)
VALUES (#referencingEntityFullName, OBJECT_ID(#referencingEntityFullName), (SELECT top 1 referenced_entity_name FROM sys.sql_expression_dependencies WHERE referencing_id=OBJECT_ID(#referencingEntityFullName) and referenced_entity_name in (select replace(TableName,'dbo.','') from #piiTablesAndColumnsTmp)))
RAISERROR('error',255,255)
END CATCH

T-sql subscript only numbers from string, cannot use create procedure, create table, etc

Similar questions have been posted in the past, but I work in a very limited environment. The offered solutions involved using create procedure, create table, etc. None of that is available here. How can I subscript only digits (0-9) that are non-continuous from strings such as '09text10more text!###11' to return 091011?. Is it possible with a combination of functions, something like SELECT Funtion1(Function2(StringField)) From Schema.Table?. Data type is varchar(30). Thank you.
---Update1---
I apologize, I am new to SQL and Stack Overflow and my question was not complete. To answer some of your questions:
-RDBMS and version:
Microsoft SQL Server Management Studio 13.0.16106.4
Microsoft Analysis Services Client Tools 13.0.1700.441
Microsoft Data Access Components (MDAC) 10.0.16299.15
Microsoft MSXML 3.0 4.0 6.0
-Yes, I need to be able to execute this within the context of a single query.
I will try the separate suggested solutions and respond accordingly.
Thank you all.
---Update2---
Damien, SELECT ##version returns Microsoft SQL Server 2016 (SP2-CU1) (KB4135048) - 13.0.5149.0 (X64) May 19 2018 09:41:57 Copyright (c) Microsoft Corporation Enterprise Edition: Core-based Licensing (64-bit) on Windows Server 2012 R2 Standard 6.3 (Build 9600: ) (Hypervisor)
Thank you.
This is a bit of a hack. :)
For my testing I used a variable but this will work with your field and a normal SELECT FROM query...
declare #textval nvarchar(max) = '09text10more text!###11'
SELECT
CASE WHEN PATINDEX('%[0-9]%', SUBSTRING(#textval,1,1))=1 THEN SUBSTRING(#textval,1,1) ELSE '' END +
CASE WHEN PATINDEX('%[0-9]%', SUBSTRING(#textval,2,1))=1 THEN SUBSTRING(#textval,2,1) ELSE '' END +
CASE WHEN PATINDEX('%[0-9]%', SUBSTRING(#textval,3,1))=1 THEN SUBSTRING(#textval,3,1) ELSE '' END +
CASE WHEN PATINDEX('%[0-9]%', SUBSTRING(#textval,4,1))=1 THEN SUBSTRING(#textval,4,1) ELSE '' END +
CASE WHEN PATINDEX('%[0-9]%', SUBSTRING(#textval,5,1))=1 THEN SUBSTRING(#textval,5,1) ELSE '' END +
CASE WHEN PATINDEX('%[0-9]%', SUBSTRING(#textval,6,1))=1 THEN SUBSTRING(#textval,6,1) ELSE '' END +
...
CASE WHEN PATINDEX('%[0-9]%', SUBSTRING(#textval,30,1))=1 THEN SUBSTRING(#textval,30,1) ELSE '' END
You basically need one case statement for every character in the field.
You should always state RDBMS and version.
Valid TSQL for SQL Server 2017 and this requirement is below.
SELECT yt.*,
ca.extractedNumbers
FROM dbo.YourTable yt
CROSS APPLY
( SELECT STRING_AGG (d.character, '' ) WITHIN GROUP ( ORDER BY d.Num )
FROM ( SELECT character = SUBSTRING(StringField, Nums.Num, 1),
Nums.Num
FROM (VALUES (01),(02),(03),(04),(05),
(06),(07),(08),(09),(10),
(11),(12),(13),(14),(15),
(16),(17),(18),(19),(20),
(21),(22),(23),(24),(25),
(26),(27),(28),(29),(30)
) Nums(Num)
WHERE Nums.Num <= LEN(yt.StringField) ) d
WHERE d.character LIKE '[0-9]'
) ca(extractedNumbers)
For previous versions you can use
WITH Nums(Num) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM (VALUES (01),(02),(03),(04),(05),
(06),(07),(08),(09),(10),
(11),(12),(13),(14),(15),
(16),(17),(18),(19),(20),
(21),(22),(23),(24),(25),
(26),(27),(28),(29),(30) )V(N)
)
SELECT yt.*,
ca.extractedNumbers
FROM dbo.YourTable yt
CROSS APPLY (SELECT d.character AS [text()]
FROM (SELECT character = SUBSTRING(StringField, Nums.Num, 1),
Nums.Num
FROM Nums
WHERE Nums.Num <= LEN(yt.StringField)) d
WHERE d.character LIKE '[0-9]'
ORDER BY d.Num
FOR XML PATH('')) ca(extractedNumbers);

PostgreSQL with ODBC: "there is no parameter $1"

I have a insert statement:
INSERT INTO billData (
tmStart, tsDuration, eCallDir, ...
) VALUES (
$1, -- tmStart
$2, -- tsDuration
$3, -- eCallDir
...
);
I use SQLPrepare to compile it, bind parameters by SQLBindParameter, and execute it by SQLExecute.
After all of there steps, the error code 42P02 (there is no parameter $1) was returned.
BTW: I'm also using the almost same code for MS SQL Server and MySQL, and these two DB are working very well, so I believe my code is correct.
PS: the PostgreSQL is v9.1; the psqlODBC is v9.01.0100-1.
============================================================
UPDATE:
And the following error occured: 42601 (syntax error at or near ",") when I'm using '?' as the parameter placholder:
INSERT INTO billData (
tmStart, tsDuration, eCallDir, ...
) VALUES (
?, -- tmStart
?, -- tsDuration
?, -- eCallDir
...
);
============================================================
UPDATE:
According to the suggestion from j.w.r, It works after adding UseServerSidePrepare=1 option to the ODBC connection string.
A lot of thanks :-)
While I am sure prepared statements works well with PostgreSQL ODBC I think something is wrong with this statement code. At first I would remove comments to make this INSERT as simple as possible. I have just tried multiline INSERT with comments and with colon at ends and it worked well. I converted a little my test Python code:
import odbc
db = odbc.odbc('odbcalias/user/password')
c = db.cursor()
r = c.execute("""INSERT INTO billData (
tmStart, tsDuration, eCallDir, ...
) VALUES (
?, -- tmStart
?, -- tsDuration
?, -- eCallDir
...
);""", (tmStart, tsDuration, eCallDir, ))
print('records inserted: %s' % (r))
Can you try it with ActivePython 2.7 (it already has odbc module)?
If it will work then enable ODBC tracing and test this program. In odbc sql trace file should be entries like:
...
python.exe odbc 924-e8c ENTER SQLExecDirect
HSTMT 00A029A8
UCHAR * 0x00B5A480 [ -3] "INSERT INTO test_table(txt) VALUES (?)\ 0"
SDWORD -3
python.exe odbc 924-e8c EXIT SQLExecDirect with return code 0 (SQL_SUCCESS)
HSTMT 00A029A8
UCHAR * 0x00B5A480 [ -3] "INSERT INTO test_table(txt) VALUES (?)\ 0"
SDWORD -3
python.exe odbc 924-e8c ENTER SQLNumResultCols
HSTMT 00A029A8
SWORD * 0x0021FAA0
python.exe odbc 924-e8c EXIT SQLNumResultCols with return code 0 (SQL_SUCCESS)
HSTMT 00A029A8
SWORD * 0x0021FAA0 (0)
python.exe odbc 924-e8c ENTER SQLRowCount
HSTMT 00A029A8
SQLLEN * 0x0021FBDC
python.exe odbc 924-e8c EXIT SQLRowCount with return code 0 (SQL_SUCCESS)
HSTMT 00A029A8
SQLLEN * 0x0021FBDC (1)
Then make similar trace for your application and try to locate what is different.
============================================================
UPDATE:
According to the suggestion from j.w.r, It works after adding UseServerSidePrepare=1 option to the ODBC connection string.
A lot of thanks :-)
Note: the ByteaAsLongVarBinary=1 option should also be added if you want to parameterize a bytea field with SQLBindParameter.

Update column to be different aggregate values

I am creating a script that for "merging" and deleting duplicate rows from a table. The table contains address information, and uses an integer field for storing information about the email as bit flags (column name lngValue). For example, lngValue & 1 == 1 means its the primary address.
There are instances of the same email being entered twice, but sometimes with different lngValues. To resolve this, I need to take the lngValue from all duplicates and assign them to one surviving record and delete the rest.
My biggest headache so far as been with the "merging" of the records. What I want to do is bitwise or all lngValues of duplicate records together. Here is what I have so far, which only finds the value of all lngValues bitwise or'ed together.
Warning: messy code ahead
declare #duplicates table
(
lngInternetPK int,
lngContactFK int,
lngValue int
)
insert into #duplicates (lngInternetPK, lngContactFK, lngValue)
(
select tblminternet.lngInternetPK, tblminternet.lngContactFK, tblminternet.lngValue from tblminternet inner join
(select strAddress, lngcontactfk, count(*) as count from tblminternet where lngValue & 256 <> 256 group by strAddress, lngcontactfk) secondemail
On tblminternet.strAddress = secondemail.strAddress and
tblminternet.lngcontactfk = secondemail.lngcontactfk
where count > 1 and tblminternet.strAddress is not null and tblminternet.lngValue & 256 <> 256 --order by lngContactFK, strAddress
)
update #duplicates set lngValue = t.val
from
(select (sum(dupes.lngValue) & 65535) as val from
(select here.lngInternetPK, here.lngContactFK, here.lngValue from tblminternet here inner join
(select strAddress, lngcontactfk, count(*) as count from tblminternet where lngValue & 256 <> 256 group by strAddress, lngcontactfk) secondemail
On here.strAddress = secondemail.strAddress and
here.lngcontactfk = secondemail.lngcontactfk
where count > 1 and here.strAddress is not null and here.lngValue & 256 <> 256) dupes, tblminternet this
where this.lngContactFK = dupes.lngContactFK
) t
where lngInternetPK in (select lngInternetPK from #duplicates)
Edit:
As requested here is some sample data:
Table Name: tblminternet
Column Names:
lngInternetPK
lngContactFK
lngValue
strAddress
Example row 1:
lngInternetPK: 1
lngContactFK: 1
lngValue: 33
strAddress: "me#myaddress.com"
Example row 2:
lngInternetPK: 2
lngContactFK: 1
lngValue: 40
strAddress: "me#myaddress.com"
If these two were merged here is the desired result:
lngInternetPK: 1
lngContactFK: 1
lngValue: 41
strAddress: "me#myaddress.com"
Other necessary rules:
Each contact can have multiple emails, but each email row must be distinct ( each email can only appear as one row).
SQL Server lacks native bitwise aggregates, that's why we need to emulate them.
The main idea here is to generate a set of bits from 0 to 15, for each bit apply the bitmask to the value and select MAX (which will give us an OR for a given bit), then select the SUM (which will merge the bit masks).
The we just update the first lngInternetPK for any given (lngContactFK, strValue) with the new value of lngValue, and delete all duplicates.
;WITH bits AS
(
SELECT 0 AS b
UNION ALL
SELECT b + 1
FROM bits
WHERE b < 15
),
v AS
(
SELECT i.*,
(
SELECT SUM(value)
FROM (
SELECT MAX(lngValue & POWER(2, b)) AS value
FROM tblmInternet ii
CROSS JOIN
bits
WHERE ii.lngContactFK = i.lngContactFK
AND ii.strAddress = i.strAddress
GROUP BY
b
) q
) AS lngNewValue
FROM (
SELECT ii.*, ROW_NUMBER() OVER (PARTITION BY lngContactFK, strAddress ORDER BY lngInternetPK) AS rn
FROM tblmInternet ii
) i
WHERE rn = 1
)
UPDATE v
SET lngValue = lngNewValue;
;WITH v AS
(
SELECT ii.*, ROW_NUMBER() OVER (PARTITION BY lngContactFK, strAddress ORDER BY lngInternetPK) AS rn
FROM tblmInternet ii
)
DELETE v
WHERE rn > 1
See this article in my blog for more detailed explanations:
SQL Server: aggregate bitwise OR
I believe the following query gets you what you want. This routine assumes a max of two duplicate addresses per contact. If there's more than one dup per contact, the query will have to be modified. I hope this helps.
Declare #tblminternet
Table
( lngInternetPK int,
lngContactFK int,
lngValue int,
strAddress varchar(255)
)
Insert Into #tblminternet
select 1, 1, 33, 'me#myaddress.com'
union
select 2, 1, 40, 'me#myaddress.com'
union
select 3, 2, 33, 'me#myaddress2.com'
union
select 4, 2, 40, 'me#myaddress2.com'
union
select 5, 3, 2, 'me#myaddress3.com'
--Select * from #tblminternet
Select Distinct
A.lngContactFK ,
A.lngValue | B.lngValue as 'Bitwise OR',
A.strAddress
From #tblminternet A, #tblminternet B
Where A.lngContactFK = B.lngContactFK
And A.strAddress = B.strAddress
And A.lngInternetPK != B.lngInternetPK
You can create SQL Server Aggregate functions in .NET that you can then implement in SQL server inline. I think this requires a minimum of SQL server 2005 and Visual Studio 2010. I did one using Visual Studio 2013 Community Edition (free even for commercial use) for use with .NET 2 and SQL Server 2005.
See the MSDN article: https://msdn.microsoft.com/en-us/library/91e6taax(v=vs.90).aspx
First you'll need to enable the CLR feature in SQL server: https://msdn.microsoft.com/en-us/library/ms131048.aspx
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO
Create a SQL Server -> SQL Server Database Project
Right-click on the new project and select Properties
Configure the targeted SQL Server version under Project Settings
Configure the targeted CLR language under SQL CLR (such as VB)
Right-click on the new project and select Add -> New Item...
When the dialog pops up, select SQL Server -> SQL CLR VB -> SQL CLR VB Aggregate
Now you can write your bitwise code in VB:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
<Serializable()> _
<Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.Native)> _
Public Structure AggregateBitwiseOR
Private CurrentAggregate As SqlTypes.SqlInt32
Public Sub Init()
CurrentAggregate = 0
End Sub
Public Sub Accumulate(ByVal value As SqlTypes.SqlInt32)
'Perform Bitwise OR against aggregate memory
CurrentAggregate = CurrentAggregate OR value
End Sub
Public Sub Merge(ByVal value as AggregateBitwiseOR)
Accumulate(value.Terminate())
End Sub
Public Function Terminate() As SqlInt32
Return CurrentAggregate
End Function
End Structure
Now deploy it: https://msdn.microsoft.com/en-us/library/dahcx0ww(v=vs.90).aspx
Build the project using the menu bar: Build -> Build ProjectName (if the build fails with error 04018 then download a new version of the data tools # http://msdn.microsoft.com/en-US/data/hh297027 or by going to the menu bar: Tools -> Extensions And Updates, then under updates select update for Microsoft SQL Server Update For Database Tooling)
Copy your compiled DLL to C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn and to C:\
Register the DLL:
CREATE ASSEMBLY [CLRTools] FROM ‘c:CLRTools.dll’ WITH PERMISSION_SET = SAFE
Create the aggregate in SQL:
CREATE AGGREGATE [dbo].[AggregateBitwiseOR](#value INT)
RETURNS INT
EXTERNAL NAME [CLRTools].[CLRTools.AggregateBitwiseOR];
If you get the error "Incorrect syntax near 'EXTERNAL'" then change the database compatibility level using following commands:
For SQL Server 2005: EXEC sp_dbcmptlevel 'DatabaseName', 90
For SQL Server 2008: EXEC sp_dbcmptlevel 'DatabaseName', 100
Test your code:
SELECT dbo.AggregateBitwiseOR(Foo) AS Foo FROM Bar
I found this article helpful: http://www.codeproject.com/Articles/37377/SQL-Server-CLR-Functions