UNION ALL, TEXT field and ORDER BY error - sql

Im having two tables with attributes like date(datetime),headline(varchar),text(text)
Now i want to UNION ALL these two tables and sort by the datetime. When doing this i'm getting the error:
Only text pointers are allowed in work tables, never text, ntext, or image columns. The query processor produced a query plan that required a text, ntext, or image column in a work table.
After trying back and forth i found out that it is the text attribute which is causing the error. But what to do? I tried casting to VARCHAR with no succes. Both tables uses text format in the text attribute.
Also when removing the ORDER BY it all works fine. What to do?
The original SQL query is below, but you can just reply to the simplified above.
SELECT id, datetime, author, headline, intro, text, type, toppriority,
secondpriority, comments, companyid, '1' source
FROM Table1
UNION ALL
SELECT AutoID AS id, Dato AS datetime,
ID COLLATE SQL_Latin1_General_CP1_CI_AS AS author, NULL AS headline,
NULL AS intro, Notat COLLATE SQL_Latin1_General_CP1_CI_AS AS text,
CAST(NotatTypeID AS VARCHAR) AS type,
NULL AS toppriority, NULL AS secondpriority, NULL AS comments,
Selskabsnummer AS companyid, '2' source
FROM Table2
WHERE (NotatTypeID = '5') OR (NotatTypeID = '6')
ORDER BY datetime DESC
Thanks in advance

One way round this is to run the union as a sup query and order the results afterwards:
SELECT * FROM
(
SELECT id, datetime, author, headline, intro, text, TYPE, toppriority,
secondpriority, comments, companyid, '1' source
FROM Table1
UNION ALL
SELECT AutoID AS id, Dato AS datetime,
ID COLLATE SQL_Latin1_General_CP1_CI_AS AS author, NULL AS headline,
NULL AS intro, Notat COLLATE SQL_Latin1_General_CP1_CI_AS AS text,
CAST(NotatTypeID AS VARCHAR) AS TYPE,
NULL AS toppriority, NULL AS secondpriority, NULL AS comments,
Selskabsnummer AS companyid, '2' source
FROM Table2
WHERE (NotatTypeID = '5') OR (NotatTypeID = '6')
) a
ORDER BY datetime DESC

What about casting the datetime field to some text field in the index? Please note that using 'datetime' and 'text' as field/alias names can be quite confusing, and a source for potential problems.

Related

How can I make a report that does a group count based on a the first character of a column?

I have this table:
CREATE TABLE [dbo].[Phrase] (
[PhraseId] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[English] NVARCHAR (MAX) NOT NULL,
[Romaji] NVARCHAR (MAX) NULL,
[EnglishAscii] AS (ascii([English])) PERSISTED,
PRIMARY KEY CLUSTERED ([PhraseId] ASC)
);
What I would like to do is to get a report that looks something like this:
A 25
B 35
C 10
D 99
...
Y 3
All the strings in the English column have a first character that is uppercase.
Can someone give me some hints as to how I can do this kind of a report?
Use LEFT string function
Select Left([English],1), Count(1)
From Yourtable
Group by Left([English],1)
or you can use SUBSTRING string function
Select Substring([English],1,1), Count(1)
From Yourtable
Group by Substring([English],1,1)
Use the UPPER keyword in order to make the first character of the column [English] in to upper case ,if it is not.
SELECT UPPER(Left([English],1)) Col,
Count(1) CNT
FROM Yourtable
GROUP BY Left([English],1)
Have a derived table which simply returns that first character. GROUP BY it's result:
select letter, count(*)
from
(
select substring([English], 1, 1) as letter
from [dbo].[Phrase]
) dt
group by letter
This way you only have to write the substring expression once. Easier to write without errors, and easier and safer to maintain/update.

Insert to sql server table from field from another table and varchar field

I need to populate reqopt table which have two varchar fields (valeur, categorie), the first shoud be field from another table (mytable) and another should be a simple varchar ('prop').
I've tried this
INSERT INTO reqopt(valeur, categorie) (select distinct name from mytable , 'prop')
I got this error Incorrect syntax near 'prop'.
INSERT INTO reqopt(valeur, categorie)
select distinct name, 'prop'
from mytable
SELECT DISTINCT NAME AS valeur,'PROP' REMARKS AS categorie
INTO reqopt
FROM MYTABLE
--WHERE CONDITION IF REQUIRED

converting a column to be empty

I have a two tables in which where one table has a datecolumn and other do not have, i have to main a union to fetch entries from both table
so as of now i am successful, but the empty fields showing invalid date like 1900-01-01, i want it should be dispayed as null, like a cast into varchar
i am trying it like this
SELECT [orderid],CAST('' as [cancelleddate] as CHAR(20)) as [cancelleddate] ,'' as [cancelledtime]
The above is throwing error eventually
and doing this way will it effect performance
SELECT orderid, cancelleddate, cancelledtime FROM TABLE1
UNION
SELECT orderid, null, null FROM TABLE2

A query to find if any two fields in a row are equal?

I have to maintain a scary legacy database that is very poorly designed. All the tables have more than 100 columns - one has 650. The database is very denormalized and I have found that often the same data is expressed in several columns in the same row.
For instance, here is a sample of columns for one of the tables:
[MEMBERADDRESS] [varchar](331) NULL,
[DISPLAYADDRESS] [varchar](max) NULL,
[MEMBERINLINEADDRESS] [varchar](max) NULL,
[DISPLAYINLINEADDRESS] [varchar](250) NULL,
[__HISTISDN] [varchar](25) NULL,
[HISTISDN] [varchar](25) NULL,
[MYDIRECTISDN] [varchar](25) NULL,
[MYISDN] [varchar](25) NULL,
[__HISTALT_PHONE] [varchar](25) NULL,
[HISTALT_PHONE] [varchar](25) NULL,
It turns out that MEMBERADDRESS and DISPLAYADDRESS have the same value for all rows in the table. The same is true for the other clusters of fields I have shown here.
It will be very difficult and time consuming to identify all cases like this manually. Is it possible to create a query that would identify if two fields have the same value in every row in a table?
If not, are there any existing tools that will help me identify these sorts of problems?
There are two approaches I see to simplify this query:
Write a script that generates your queries - feed your script the name of the table and the suspected columns, and let it produce a query that checks each pair of columns for equality. This is the fastest approach to implement in a one-of situation like yours.
Write a query that "normalizes" your data, and search against it - self-join the query to itself, then filter out the duplicates.
Here is a quick illustration of the second approach:
SELECT id, name, val FROM (
SELECT id, MEMBERADDRESS as val,'MEMBERADDRESS' as name FROM MyTable
UNION ALL
SELECT id, DISPLAYADDRESS as val,'DISPLAYADDRESS' as name FROM MyTable
UNION ALL
SELECT id, MEMBERINLINEADDRESS as val,'MEMBERINLINEADDRESS' as name FROM MyTable
UNION ALL
...
) first
JOIN (
SELECT id, MEMBERADDRESS as val,'MEMBERADDRESS' as name FROM MyTable
UNION ALL
SELECT id, DISPLAYADDRESS as val,'DISPLAYADDRESS' as name FROM MyTable
UNION ALL
SELECT id, MEMBERINLINEADDRESS as val,'MEMBERINLINEADDRESS' as name FROM MyTable
UNION ALL
...
) second ON first.id=second.id AND first.value=second.value
There is a lot of manual work for 100 columns (at least it does not grow as N^2, as in the first approach, but it is still a lot of manual typing). You may be better off generating the selects connected with UNION ALL using a small script.
The following approach uses unpivot to create triples. It makes some assumptions: values are not null; each row has an id; and columns have compatible types.
select t.which, t2.which
from (select id, which, value
from MEMBERADDRESS
unpivot (value for which in (<list of columns here>)) up
) t full outer join
(select id, which, value
from MEMBERADDRESS
unpivot (value for which in (<list of columns here>)) up
) t2
on t.id = t2.id and t.which <> t2.which
group by t.which, t2.which
having sum(case when t.value = t2.value then 1 else 0 end) = count(*)
It works by creating a new table with three columns: id, which column, and the value in the column. It then does a self join on id (to keep comparisons within one row) and value (to get matching values). This self-join should always match, because the columns are the same in the two halves of the query.
The having then counts the number of values that are the same on both sides for a given pair of columns. When all these are the same, then the match is successful.
You can also leave out the having clause and use something like:
select t.which, t2.which, sum(case when t.value = t2.value then 1 else 0 end) as Nummatchs,
count(*) as NumRows
To get more complete information.

SQL searching two columns for best results

I would like to perform an sql search and I would like to get best results. I tried some things but they didn't work well. I have got two columns named subject and content
For example we will search "search this keywords" text on subject and content area. First I'm searching "search this keywords" then searching "search" and "this" and "keywords"
I would like to retrieve subject's results on top and I would like to retrieve best results liking "search this keywords".My query sometimes works well sometimes not.
How should I write this query
Thanks..
I think you're saying that you want to perform multiple SQL queries against your database and then combine the results and set a "weighting" to a subject match over a content match.
select messageid, textstring, max(weight) from (
-- exact subject match
select messageid, substr(subject,1,100) textstring, 100 weight
from mytable
where subject='search this keywords'
union
-- partial subject match
select messageid, substr(subject,1,100), 90 weight
from mytable
where subject like '%search this keywords%'
union
select messageid, substr(subject,1,100), 80 weight
from mytable
where subject like '%search%'
union
select messageid, substr(subject,1,100), 80 weight
from mytable
where subject like '%this%'
union
select messageid, substr(subject,1,100), 80 weight
from mytable
where subject like '%keywords%'
union
-- partial content match
select messageid, substr(content,1,100), 70 weight
from mytable
where content like '%search this keywords%'
union
select messageid, substr(content,1,100), 60 weight
from mytable
where content like '%search%'
union
select messageid, substr(content,1,100), 60 weight
from mytable
where content like '%this%'
union
select messageid, substr(content,1,100), 60 weight
from mytable
where content like '%keywords%'
)
group by
messageid, textstring,
Try this
select * from (
Select sch, rank,
case when sch like '%search this keywords%' then 0
when sch like '%search%' then 1
when sch like '%this%' then 2
when sch like '%keywords%' then 3 end ord
from
(
select subject as sch, 1 as rank from mytable
union all
select content, 2 as rank from mytable
) as x
) as y
where ord is not null
order by rank, ord
Implementing a simple 'Full Text Search' like tables would be a way.
CREATE TABLE YourTable (id int, subject varchar(256), content varchar(8000))
CREATE TABLE Keywords (key_id int, keyw varchar(50), relevanceModifier float)
CREATE TABLE SubjectsKeywords (key_fk int, yourTable_fk int, quantity int)
CREATE TABLE ContentKeywords (key_fk int, yourTable_fk int, quantity int)
When you insert in YourTable, fire a trigger to:
Split subject and content columns by spaces, commas, etc into words.
Optionally, avoid "stop words" like "the", "they", "to", etc. This is called stemming if i'm not mistaken.
Each word should be inserted in tables SubjectsKeywords, ContentKeywords and Keywords.
Optionally, set relevanceModifier. A very simple criteria would be to use the string length.
Optionally, count each ocurrence and track it quantity fields.
Then your query would be like this:
select max(t.relevance), yourtable.id, MAX([subject]), MAX(content)
from
(
/* exact match and 'contains' match */
select 100 as relevance, id
from YourTable
where [subject] like '%search this keywords%'
UNION
/* keyword match */
select 70 as relevance, yt.id
from YourTable as yt
join SubjectsKeywords on id = yourTable_fk
join Keywords as k on k.id = key_fk
where keyw in ('search', 'this', 'keywords')
UNION
select 40 as relevance, id
from YourTable
where [subject] like '%search this keywords%'
UNION
select 10 as relevance, yt.id
from YourTable as yt
join ContentKeywords on yt.id = yourTable_fk
join Keywords as k on k.id = key_fk
where keyw in ('search', 'this', 'keywords')
) as T
join yourtable on t.id = yourtable.id
group by t.id
order by max(relevance) desc
, yourtable.id ASC /*So that the result will always be in the same order*/
Notes:
Trigger is a way to do it if you have little control of you application or if it is a maintenance nightmare.
Later it you could improve it by adding a soundex, so that, you can search even mispelled keywords.
RelevanceModifier, Quantity field can be use to calculate more relevant results.
As it may be fast enough, it may be usefull as an autocomplete feature for your application, in which case you'd like to limit the results to let say 256 at most.
I hope this gives you and idea, and so you decide what suits you best.