I am trying to count all possible same values from 5 columns with SQL.
For example, if there are matching values in 3 columns, it will return 3 in a custom column just like the below example:
A B C D E Count
1 1 1 1 1 5
1 1 N N N 2
Are there any ways to make these kinds of comparing with SQL?
Assuming N represents NULL and not 'N' you could Pivot the data out and then COUNT it:
CREATE TABLE #YourTable (A tinyint,
B tinyint,
C tinyint,
D tinyint,
E tinyint);
INSERT INTO #YourTable (A,
B,
C,
D,
E)
VALUES(1,1,1,1,1),
(1,1,NULL,NULL,NULL);
GO
SELECT YT.A,
YT.B,
YT.C,
YT.D,
YT.E,
(SELECT COUNT(V.Col)
FROM (VALUES(YT.A),(YT.B),(YT.C),(YT.D),(YT.E))V(Col)) AS [Count]
FROM #YourTable YT;
GO
DROP TABLE #YourTable
But, like Tim said, I agree; you should really have normalised data, if this is something you actually need to do.
Related
I have a table like this where I keep a list of appointments which increment based on the combination of A and B:
ID A B Appointment Count
-----------------------------
1 abc 0 2010-10-20 1
2 abc 0 2010-10-25 2
3 abc 0 2010-10-30 3
3 abc 1 2010-10-30 1
4 xyz 1 2010-08-18 1
5 xyz 1 2010-08-19 2
6 xyz 1 2010-08-20 3
And a function like this:
CREATE FUNCTION dbo.GenerateCount
(
#id int,
#A int,
#B int,
#appt_date date
)
RETURNS Int
AS
BEGIN
RETURN
(
SELECT COUNT(*)
FROM dbo.test_seq
WHERE patient_id = #A
AND B = #B
AND id <= #id
AND appt_date <= #appt_date
)
END
With data inserted like this:
CREATE TABLE [dbo].[test_seq](
[id] [int] IDENTITY(1,1) NOT NULL,
[A] [int] NOT NULL,
[B] [int] NOT NULL,
[appt_date] [date] NOT NULL,
[count] AS dbo.GenerateCount(id, A, B, appt_date)
)
When I insert a new entry in the table, it increments the count as expected. However if I insert a new entry with a date in the middle, say if I want to add:
ID A B Appointment Count
-----------------------------
1 abc 0 2010-10-21
it has the correct count assigned, but the other rows don't get updated. How can I trigger a table update for all the other records after that date so they are corrected with the relevant count values?
I tried creating a trigger on insert/update/delete, but that only applies to the row being inserted and not the whole table.
If I get this correctly, the simple answer is: Don't!
A SQL-Server-table is not Excel...
You must decide
Do you want to set a value persistantly (in other words: a kind of key)?
Do you just want to number the rows for the moment?
Create a VIEW upon your table (according to the approach you find in your last question).
This will compute the correct numbers whenever you call that.
Why not just create a view
SELECT seq.*
, row_number() over (partition by A, B order by Appointment, ID) as [count]
FROM dbo.test_seq seq
Why the AND id <= #id? That is just going to break stuff if you do insert a date in the middle.
I have one source table containing 50 columns and two destination tables containing 25 columns each.
Source table A
Destination Tables B and C
Table B has an identity as a primary key. I need to insert the first 25 columns in table B, get the value of the primary key and insert the remaining values in table C with this primary key value.
It would be easy if it was single inserts but its a bulk insert. For eg:
insert into b(c2,c3,c4)
select c2,c3,c4 from a
(Now i need to pick the id from b)
insert into c
select id,c5,c6,c7 from a
and the rows need to match.
I`m a novice at SQL. Sorry if there are bad examples.
Use MERGE with OUTPUT:
CREATE TABLE A(x INT, y int, z int)
CREATE TABLE B(x INT, y int, z int)
CREATE TABLE C(x INT, y int, z int)
GO
INSERT INTO A VALUES(1, 1, 1), (2, 2, 2)
MERGE INTO B dest
USING(SELECT * FROM A) src ON dest.x = src.x
WHEN NOT MATCHED BY TARGET THEN
INSERT VALUES (src.x, src.y, src.z)
OUTPUT Inserted.x, Inserted.y, Inserted.z INTO C;
SELECT * FROM A
SELECT * FROM B
SELECT * FROM C
Output:
A
x y z
1 1 1
2 2 2
B
x y z
1 1 1
2 2 2
C
x y z
1 1 1
2 2 2
IDENT_CURRENT('Your B Table NAME')
It will get the last primary key of B Table
I have two tables. STOCK_HIST contains a time-sequence (DSEQKEY) of values (PRHIGH). The other table (PHASE_KEYS_D) contains beginning and ending keys for STOCK_HIST representing various periods of time in STOCK_HIST marked by PHASE_KEYS_D.BEGKEY through PHASE_KEYS_D.ENDKEY. I am trying to get a query working that will return the highest PRHIGH in STOCK_HIST between the BEGKEY and ENDKEY of each row in PHASE_KEYS_D. I tried the following but it does not always return what it should.
select dseqkey,prhigh
from STOCK_HIST
where prhigh in
(
select max(prhigh)
from STOCK_HIST A, PHASE_KEYS_D B
where a.dseqkey between b.begkey and b.endkey
group by b.begkey
)
order by dseqkey desc
CREATE TABLE STOCK_HIST (DSEQKEY INT, PRHIGH MONEY)
DSEQKEY PRHIGH
1 1432.22
2 1433.10
3 1435.55
4 1440.21
5 1422.20
6 1415.10
7 1401.99
8 1433.10
CREATE TABLE PHASE_KEYS_D (BEGKEY INT, ENDKEY INT)
BEGKEY ENDKEY
1 3
4 5
6 8
Thanks in advance for the help!
One of your problems here is that neither table, and especially the PHASE_KEYS_D table, have primary keys. It's almost always a good idea for all tables to have primary key, and preferably non-intelligent keys (meaning no data is represented in them, they're just dumb ids).
This is how straight forward the query becomes when you have a primary key:
alter table phase_keys_id add phase_id int not null identity (1,1)
select p.phase_id,max(h.prhigh)
from
phase_keys_d p
inner join stock_hist h on h.DSEQKEY between p.BEGKEY and p.ENDKEY
group by p.phase_id
And if you wanted to show the max with each detail row:
;with z as (
select h.dseqkey,p.phase_id,h.prhigh
from
phase_keys_d p
inner join stock_hist h on h.DSEQKEY between p.BEGKEY and p.ENDKEY
)
select
z.dseqkey, z.prhigh, y.mx
from
z
inner join (select phase_id,mx=max(prhigh) from z group by phase_id) y
on y.phase_id = z.phase_id
I have a SELECT that can return hundreds of rows from a table (table can be ~50000 rows). My app is interested in knowing the number of rows returned, it means something important to me, but it actually uses only the top 5 of those hundreds of rows. What I want to do is limit the SELECT query to return only 5 rows, but also tell my app how many it would have returned (the hundreds). This is the original query:
SELECT id, a, b, c FROM table WHERE a < 2
Here is what I came up with - a CTE - but I don't feel comfortable with the total row count appearing in every column. Ideally I would want a result set of the TOP 5 and a returned parameter for the total row count.
WITH Everything AS
(
SELECT id, a, b, c FROM table
),
DetermineCount AS
(
SELECT COUNT(*) AS Total FROM Everything
)
SELECT TOP (5) id, a, b, c, Total
FROM Everything
CROSS JOIN DetermineCount;
Can you think of a better way?
Is there a way in T-SQl to return the affected row count of a select top query before the top was applied? ##rowcount would return 5 but I wonder if there is a ##rowcountbeforetop sort of thing.
Thanks in advance for your help.
** Update **
This is what I'm doing now and I kind of like it over the CTE although CTEs as so elegant.
-- #count is passed in as an out param to the stored procedure
CREATE TABLE dbo.#everything (id int, a int, b int, c int);
INSERT INTO #everything
SELECT id, a, b, c FROM table WHERE a < 2;
SET #count = ##rowcount;
SELECT TOP (5) id FROM #everything;
DROP TABLE #everything;
Here's a relatively efficient way to get 5 random rows and include the total count. The random element will introduce a full sort no matter where you put it.
SELECT TOP (5) id,a,b,c,total = COUNT(*) OVER()
FROM dbo.mytable
ORDER BY NEWID();
Assuming you want the top 5 ordering by id ascending, this will do it with a single pass through your table.
; WITH Everything AS
(
SELECT id
, a
, b
, c
, ROW_NUMBER() OVER (ORDER BY id ASC) AS rn_asc
, ROW_NUMBER() OVER (ORDER BY id DESC) AS rn_desc
FROM <table>
)
SELECT id
, a
, b
, c
, rn_asc + rn_desc - 1 AS total_rows
FROM Everything
WHERE rn_asc <= 5
** Update **
This is what I'm doing now and I kind of like it over the CTE although CTEs as so elegant. Let me know what you think. Thanks!
-- #count is passed in as an out param to the stored procedure
CREATE TABLE dbo.#everything (id int, a int, b int, c int);
INSERT INTO #everything
SELECT id, a, b, c FROM table WHERE a < 2;
SET #count = ##rowcount;
SELECT TOP (5) id FROM #everything;
DROP TABLE #everything;
I have the following
Chars
A
C
W
B
J
M
How can I insert some sequential numbers so that after insertion of the numbers the order of characters will not change?
I mean if I use row_number(), the output Character order is changing like
select
ROW_NUMBER() over(order by chars) as id,
t.* from #t t
Output:
id chars
1 A
2 B
3 C
4 J
5 M
6 W
My desired expectation is
id chars
1 A
2 C
3 W
4 B
5 J
6 M
Also, I cannot use any identity field like id int identity because I am in the middle of a query and I need to maintain a inner join for achieving something.
I hope I do make myself clear.
Please help.
Thanks in advance
There is no implicit ordering of rows in SQL. If some ordering is desired, be it order in which items were inserted or any other order, it must be supported by a user-defined column.
In other words, the SQL standard doesn't require the SQL implementations to maintain any order. On the other hand the ORDER BY clause in a SELECT statement can be used to specify the desired order, but such ordering is supported by the values in a particular (again, user defined) column.
This user defined column may well be an auto-incremented column for which SQL assigns incremental (or otherwise) values to, and this may be what you need.
Maybe something like...
CREATE TABLE myTable
(
InsertID smallint IDENTITY(1,1),
OneChar CHAR(1),
SomeOtherField VARCHAR(20)
-- ... etc.
)
INSERT INTO myTable (OneChar, SomeOtherField) VALUES ('A', 'Alpha')
INSERT INTO myTable (OneChar, SomeOtherField) VALUES ('W', 'Whiskey')
INSERT INTO myTable (OneChar, SomeOtherField) VALUES ('B', 'Bravo')
-- ... etc.
SELECT OneChar
FROM myTable
ORDER BY InsertId
'A'
'W'
'B'
--...